Alga

Sample generative art as a playground of Tweakpane, compact GUI for fine-tuning parameters and monitoring value changes.

by cocopon

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.8.4/pixi.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/tweakpane.min.js"></script>

CSS

@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:400,500,700');
html,
body {
  background: #111;
  margin: 0;
  overflow: hidden;
  width: 600px;
}

body>canvas {
  left: 50%;
  margin-left: -300px;
  margin-top: -300px;
  position: absolute;
  top: 50%;
}

/* Translucent pane */
:root {
  --tp-base-background-color: #2f313740;
}
.tp-rotv {
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

JavaScript

// Input parameters
const INPUTS = {
  color: '#fff',
  count: 10000,
  mainFreq: 24,
  maxSize: 180,
  minSize: 130,
  speed: 2 * 1e-4,
  subFreq: 300,
  subLen: 0.2,
};
// Output parameters
const OUTPUTS = {
  json: '',
};
// Canvas size
const SIZE = 600;

function map(v, s1, e1, s2, e2) {
  return s2 + (v - s1) / (e1 - s1) * (e2 - s2);
}

function saveCanvas(canvasElem) {
  const anchor = document.createElement('a');
  anchor.download = 'capture.png';
  anchor.href = canvasElem.toDataURL();
  anchor.click();
}

// Custom shader
class EffectFilter extends PIXI.Filter {
  constructor() {
    super(
      null,
      `
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform vec4 filterArea;
uniform vec2 screenSize;
uniform float abberation;

vec2 applyOffset(float ofs) {
  return ((vTextureCoord.xy * filterArea.xy - screenSize / 2.0) * (1.0 + ofs) + screenSize / 2.0) / filterArea.xy;
}

void main(void) {
  float pr = texture2D(uSampler, applyOffset(0.0)).r;
  float pg = texture2D(uSampler, applyOffset(-abberation)).g;
  float pb = texture2D(uSampler, applyOffset(abberation * -2.0)).b;

	gl_FragColor = vec4(pr, pg, pb, 1.0);
}
			`, {
        abberation: {
          type: 'float',
          value: 0,
        },
        screenSize: {
          type: 'vec2',
          value: [0, 0],
        },
      }
    );

    this.padding = 0;
  }

  get abberation() {
    return this.uniforms.abberation;
  }

  set abberation(value) {
    this.uniforms.abberation = value;
  }

  get screenSize() {
    return this.uniforms.screenSize;
  }

  set screenSize(value) {
    this.uniforms.screenSize = value;
  }
}

// PIXI.js application
const app = new PIXI.Application({
  height: SIZE,
  preserveDrawingBuffer: true,
  width: SIZE,
});
app.stage.filterArea = new PIXI.Rectangle(0, 0, SIZE, SIZE);
document.body.appendChild(app.renderer.view);

const filter = new EffectFilter();
filter.abberation = 0.015;
filter.screenSize = [SIZE,...