Published
Edited
Nov 1, 2018
1 fork
51 stars
Insert cell
Insert cell
Insert cell
Insert cell
canvas = DOM.canvas(width, 720)
Insert cell
{
const t1 = newTexture(seed);
const t2 = newTexture();
const fb1 = newFramebuffer(t1);
const fb2 = newFramebuffer(t2);
for (let i = 0; true; ++i) {
gl.useProgram(timestepProgram);
gl.viewport(0, 0, W, H);
for (let j = 0; j < 8; ++j) {
gl.bindTexture(gl.TEXTURE_2D, t1);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
gl.bindTexture(gl.TEXTURE_2D, t2);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb1);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
gl.useProgram(renderProgram);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.bindTexture(gl.TEXTURE_2D, t2);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
yield i;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
seed = {
restart;
const a = new Float32Array(4 * W * H);

for (let y = 0; y < H; ++y) {
for (let x = 0; x < W; ++x) {
const i = W * y + x << 2;
a[i + 0] = 0.9;
}
}

function dot(cx, cy, r = 5) {
const r2 = r ** 2;
for (let y = cy - r; y < cy + r; ++y) {
for (let x = cx - r; x < cx + r; ++x) {
if ((x - cx) ** 2 + (y - cy) ** 2 < r2) {
const i = W * y + x << 2;
a[i + 1] = 0.9;
}
}
}
}

for (let i = 0; i < 20; ++i) {
dot(W * Math.random(), H * Math.random());
}

return a;
}
Insert cell
function newTexture(source) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, W, H, 0, gl.RGBA, gl.FLOAT, source);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
return texture;
}
Insert cell
function newFramebuffer(texture) {
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
return framebuffer;
}
Insert cell
Insert cell
timestepShader = newShader(gl.FRAGMENT_SHADER, `
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_size;
uniform float u_F;
uniform float u_K;
const float D_a = 0.2;
const float D_b = 0.1;
const float D_t = 1.0;

void main() {
vec2 position = gl_FragCoord.xy;
vec3 color = texture2D(u_image, position / u_size).xyz;
vec2 laplacian = -4.0 * color.xy
+ texture2D(u_image, (position + vec2(0.0, 1.0)) / u_size).xy
+ texture2D(u_image, (position + vec2(1.0, 0.0)) / u_size).xy
+ texture2D(u_image, (position + vec2(0.0, -1.0)) / u_size).xy
+ texture2D(u_image, (position + vec2(-1.0, 0.0)) / u_size).xy;
gl_FragColor = vec4(
color.x + D_t * (D_a * laplacian.x - color.x * color.y * color.y + u_F * (1.0 - color.x)),
color.y + D_t * (D_b * laplacian.y + color.x * color.y * color.y - (u_K + u_F) * color.y),
clamp(color.z + (color.x - 0.6) * 0.002, 0.0, 1.0),
1.0
);
}`)
Insert cell
renderShader = newShader(gl.FRAGMENT_SHADER, `
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_size;
const float pi = 3.14159265359;

vec3 cubehelix(float x, float y, float z) {
float a = y * z * (1.0 - z);
float cosh = cos(x + pi / 2.0);
float sinh = sin(x + pi / 2.0);
return vec3(
(z + a * (1.78277 * sinh - 0.14861 * cosh)),
(z - a * (0.29227 * cosh + 0.90649 * sinh)),
(z + a * (1.97294 * cosh))
);
}

void main() {
vec2 c = texture2D(u_image, gl_FragCoord.xy / u_size).xz;
float t = smoothstep(0.5, 0.7, c.x);
gl_FragColor = vec4(cubehelix(c.y, 1.0, t + c.y - t * c.y), 1.0);
}`)
Insert cell
vertexShader = newShader(gl.VERTEX_SHADER, `
attribute vec2 a_position;

void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}`)
Insert cell
function newShader(type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw new Error("cannot compile shader");
return shader;
}
Insert cell
Insert cell
renderProgram = newProgram(vertexShader, renderShader)
Insert cell
timestepProgram = newProgram(vertexShader, timestepShader)
Insert cell
{
gl.useProgram(timestepProgram);
gl.uniform1f(gl.getUniformLocation(timestepProgram, "u_F"), FK.feed);
gl.uniform1f(gl.getUniformLocation(timestepProgram, "u_K"), FK.kill);
}
Insert cell
function newProgram(vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw new Error("cannot link program");
const a_position = gl.getAttribLocation(program, "a_position");
const u_size = gl.getUniformLocation(program, "u_size");
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(a_position);
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(u_size, W, H);
return program;
}
Insert cell
gl = {
const gl = canvas.getContext("webgl", {alpha: false, antialias: false, depth: false});
if (!gl) throw new Error("WebGL unsupported");
if (!gl.getExtension("OES_texture_float")) throw new Error("OES_texture_float unsupported");
if (gl.getParameter(gl.MAX_TEXTURE_SIZE) < W) throw new Error("big textures unsupported");
return gl;
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more