<div id="container">
  <div id="control-panel">
    <h3>effect</h3>
    <div class="input">
      <label>type</label>
      <select id="type" >
        <option>brightnessContrast</option>
        <option>bulgePinch</option>
        <option>colorHalftone</option>
        <option>dotScreen</option>
        <option>edgeWork</option>
        <option>hexagonalPixelate</option>
        <option>hueSaturation</option>
        <option>ink</option>
        <option>magnify</option>
        <option>noise</option>
        <option>sepia</option>
        <option>swirl</option>
        <option selected>tiltShift</option>
        <option>triangleBlur</option>
        <option>vibrance</option>
        <option>vignette</option>
        <option>zoomBlur</option>
      </select>
      <span></span>
    </div>
    <hr />
  </div>
  <div id="deckgl"></div>
</div>
#container {
  position: fixed;
  width: 100vw;
  height: 100vh;
  top: 0;
  left: 0;
  background: #eee;
  display: flex;
}

#deckgl {
  flex-grow: 1;
}

#control-panel {
  font-family: Helvetica, Arial, sans-serif;
  background: #fff;
  padding: 20px;
  z-index: 1;
  line-height: 1.6;
  overflow-y: auto;
}
#control-panel div {
  padding-left: 10px;
}

.input {
  display: flex;
}
.input label {
  width: 120px;
}
.input input, .input select {
  width: 140px;
  margin: 0 8px;
}
.input span {
  width: 40px;
}
hr {
  border-bottom: none;
  margin: 12px;
}
let inputs = [];
let selectedEffect = null;
const effectSelector = document.querySelector('select');

const {noise, colorHalftone, tiltShift} = luma;

const data = [
  {position: [-5, 0, -5], color: [255, 255, 128]},
  {position: [5, 0, -5], color: [255, 128, 128]},
  {position: [-5, 0, 5], color: [128, 255, 128]},
  {position: [5, 0, 5], color: [128, 128, 255]},
];

const deckgl = new deck.DeckGL({
  container: document.getElementById('deckgl'),
  views: [new deck.OrbitView({fov: 50})],
  initialViewState: {target: [0, 0, 0], rotationX: 0, rotationOrbit: 45, zoom: 5},
  controller: true,
  layers: [
    new deck.SimpleMeshLayer({
      id: 'spheres',
      coordinateSystem: deck.COORDINATE_SYSTEM.IDENTITY,
      opacity: 1,
      data,
      mesh: new luma.CubeGeometry(),
      material: {
        ambient: 0.2,
        diffuse: 0.8
      },
      getPosition: d => d.position,
      getColor: d => d.color,
      getScale: [3, 3, 3],
      getOrientation: (d, {index}) => [index * 10, -index * 30, index * 5]
    })
  ]
});

effectSelector.oninput = redraw;
redraw();

function redraw() {
  const settings = updateInputs(effectSelector.value);
  
  deckgl.setProps({
    effects: [new deck.PostProcessEffect(luma[selectedEffect], settings)]
  });
}

function updateInputs(effectName) {
  if (selectedEffect !== effectName) {
    selectedEffect = effectName;

    inputs.forEach(el => el.remove());
    inputs.length = 0;

    const {uniforms} = luma[effectName];

    for (const key in uniforms) {
      const defaultValue = uniforms[key];

      if (Array.isArray(defaultValue)) {
        inputs.push(createInput(key + '.x', defaultValue[0]));
        inputs.push(createInput(key + '.y', defaultValue[1]));
      } else {
        inputs.push(createInput(key, defaultValue));
      }
    }
  }
  const settings = {};
  inputs.forEach(el => {
    const value = +el.querySelector('input').value;
    if (el.id.endsWith('.x')) {
      settings[el.id.replace('.x', '')] = [value];
    } else if (el.id.endsWith('.y')) {
      settings[el.id.replace('.y', '')][1] = value;
    } else {
      settings[el.id] = value;
    }
    el.querySelector('span').innerText = value;
  });
  return settings;
}

function createInput(name, defaultValue) {
  let min, max, value;
  if (typeof defaultValue === 'number') {
    min = 0;
    max = defaultValue <= 1 ? 1 : 50;
    value = defaultValue;
  } else {
    if ('value' in defaultValue) {
      value = defaultValue.value;
    }
    if ('min' in defaultValue) {
      min = defaultValue.min;
    } else if ('softMin' in defaultValue) {
      min = defaultValue.softMin;
    }
    if ('max' in defaultValue) {
      max = defaultValue.max;
    } else if ('softMax' in defaultValue) {
      max = defaultValue.softMax;
    }
  }
  
  const container = document.createElement('div');
  container.id = name;
  container.className = 'input';

  const label = document.createElement('label');
  label.innerText = name;
  container.appendChild(label);

  const input = document.createElement('input');
  input.type = 'range';
  input.setAttribute('step', 0.01);
  input.setAttribute('min', min);
  input.setAttribute('max', max);
  input.setAttribute('value', value);
  input.oninput = redraw;
  container.appendChild(input);
  
  const output = document.createElement('span');
  container.appendChild(output);
  
  document.getElementById('control-panel').appendChild(container);
  
  return container;
}

External CSS

  1. https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css

External JavaScript

  1. https://unpkg.com/deck.gl@^8.9.0/dist.min.js
  2. https://unpkg.com/@luma.gl/shadertools@^8.5.0/dist/dist.min.js