Published
Edited
Nov 3, 2020
Fork of Metaballs
9 forks
Importers
25 stars
Insert cell
Insert cell
canvas = DOM.canvas(width, height)
Insert cell
k = Math.sin(now / 5000) * 5
Insert cell
setup = {
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.enableVertexAttribArray(a_vertex);
gl.vertexAttribPointer(a_vertex, 2, gl.FLOAT, false, 0, 0);
}
Insert cell
draw = {
setup;
gl.uniform1f(u_k, k);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
}
Insert cell
fragmentShader = {
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, `
precision highp float;
uniform float u_k;

const float PI = 3.14159265359;

// https://github.com/d3/d3-color
vec3 cubehelix(vec3 c) {
float a = c.y * c.z * (1.0 - c.z);
float cosh = cos(c.x + PI / 2.0);
float sinh = sin(c.x + PI / 2.0);
return vec3(
(c.z + a * (1.78277 * sinh - 0.14861 * cosh)),
(c.z - a * (0.29227 * cosh + 0.90649 * sinh)),
(c.z + a * (1.97294 * cosh))
);
}

// https://github.com/d3/d3-scale-chromatic
vec3 cubehelixDefault(float t) {
return cubehelix(vec3(mix(300.0 / 180.0 * PI, -240.0 / 180.0 * PI, t), 0.5, t));
}

// https://github.com/d3/d3-scale-chromatic
vec3 cubehelixRainbow(float t) {
if (t < 0.0 || t > 1.0) t -= floor(t);
float ts = abs(t - 0.5);
return cubehelix(vec3((360.0 * t - 100.0) / 180.0 * PI, 1.5 - 1.5 * ts, 0.8 - 0.9 * ts));
}

float sinh(float x) {
return (exp(x) - exp(-x)) / 2.0;
}

float cosh(float x) {
return (exp(x) + exp(-x)) / 2.0;
}

// https://golang.org/src/math/cmplx/phase.go
float complex_phase(vec2 x) {
return atan(x.y, x.x);
}

// https://golang.org/src/math/cmplx/sin.go
vec2 complex_sin(vec2 x) {
return vec2(sin(x.x) * cosh(x.y), cos(x.x) * sinh(x.y));
}

// https://golang.org/src/math/cmplx/isinf.go
vec2 complex_inf() {
return vec2(1.0 / 0.0, 1.0 / 0.0);
}

// https://golang.org/src/math/cmplx/abs.go
// https://golang.org/src/math/hypot.go
float complex_abs(vec2 x) {
float p = x.x;
float q = x.y;
if (p < 0.0) p = -p;
if (q < 0.0) q = -q;
if (p < q) { float t = p; p = q; q = t; }
if (p == 0.0) return 0.0;
q = q / p;
return p * sqrt(1.0 + q * q);
}

// https://golang.org/src/math/cmplx/pow.go
vec2 complex_pow(vec2 x, vec2 y) {
float r;
float i;
if (x.x == 0.0 && x.y == 0.0) {
r = y.x;
i = y.y;
if (r == 0.0) return vec2(1.0, 0.0);
if (r < 0.0) {
if (i == 0.0) return vec2(1.0 / 0.0, 0.0);
return complex_inf();
}
if (r > 0.0) return vec2(0.0, 0.0);
}
float modulus = complex_abs(x);
if (modulus == 0.0) return vec2(0.0, 0.0);
r = pow(modulus, y.x);
float arg = complex_phase(x);
float theta = y.x * arg;
if (y.y != 0.0) {
r *= exp(-y.y * arg);
theta += y.y * log(modulus);
}
return vec2(r * cos(theta), r * sin(theta));
}

void main(void) {
vec2 z = (gl_FragCoord.xy - 932.0 / 2.0) / 256.0;
z = complex_sin(complex_pow(z, vec2(5.0, 0.0)) - u_k);
float t = complex_phase(z) / PI;
gl_FragColor = vec4(cubehelixRainbow(t), 1.0);
}
`);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
throw new Error(gl.getShaderInfoLog(shader));
}
Insert cell
vertexShader = {
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, `
attribute vec2 a_vertex;
void main(void) {
gl_Position = vec4(a_vertex, 0.0, 1.0);
}
`);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
throw new Error(gl.getShaderInfoLog(shader));
}
Insert cell
program = {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (gl.getProgramParameter(program, gl.LINK_STATUS)) return program;
throw new Error(gl.getProgramInfoLog(program));
}
Insert cell
vertexBuffer = {
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, Float32Array.of(-1, -1, +1, -1, +1, +1, -1, +1), gl.STATIC_DRAW);
return buffer;
}
Insert cell
a_vertex = gl.getAttribLocation(program, "a_vertex")
Insert cell
u_k = gl.getUniformLocation(program, "u_k")
Insert cell
gl = canvas.getContext("webgl", {depth: false})
Insert cell
height = 932
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more