console.clear() window.regl ? run(null, regl) : reglLib({onDone: run}) function randomWalk(n){ var current = 0 var s = 1/Math.sqrt(n) var length = Math.floor(1/s)*2 return function(i){ var x = i % length current = x ? current + (Math.random() - .5)*3 : 0 return [(x - length/2)*s, current*s, i] } } function run (err, regl) { window.regl = regl let n = 100000 datasets = [] let colorBasis let datasetPtr = 0 let pointRadius = 2 let lastSwitchTime = 0 let switchInterval = 1 let switchDuration = 1 const createDatasets = () => { // This is a cute little pattern that *either* creates a buffer or updates // the existing buffer since both the constructor and the current instance // can be called as a function. datasets = [randomWalk, randomWalk, randomWalk, randomWalk, randomWalk].map((func, i) => (datasets[i] || regl.buffer)(vectorFill(ndarray([], [n, 2]), func(n))) ) // This is just a list from 1 to 0 for coloring: colorBasis = (colorBasis || regl.buffer)(linspace(ndarray([], [n]), 1, 0)) } // Initialize: createDatasets() const drawPoints = regl({ vert: ` precision mediump float; attribute vec2 xy0, xy1; attribute float basis; varying float t; uniform float aspect, interp, radius; void main () { t = basis; // Interpolate between the two positions: // float s = clamp(interp*2.0 + t -1.0, 0, 1); // float s = clamp(interp*2.0 + t -1.0, 0, 1); vec2 pos = mix(xy0, xy1, interp); gl_Position = vec4(pos.x, pos.y * aspect, 0, 1); gl_PointSize = radius; } `, frag: ` precision mediump float; ${glslViridis} varying float t; void main () { gl_FragColor = viridis(t); // gl_FragColor = vec4(t, t, 0, 1); } `, depth: {enable: false}, attributes: { // Pass two buffers between which we ease in the vertex shader: xy0: () => datasets[datasetPtr % datasets.length], xy1: () => datasets[(datasetPtr + 1) % datasets.length], basis: () => colorBasis }, uniforms: { radius: () => pointRadius, aspect: ctx => ctx.viewportWidth / ctx.viewportHeight, // The current interpolation position, from 0 to 1: interp: (ctx, props) => Math.max(0, Math.min(1, props.interp)) }, primitive: 'point', count: () => n }) if (window.regltick) window.regltick.cancel() window.regltick = regl.frame(({time}) => { // Check how long it's been since the last switch, and cycle the buffers // and reset the timer if it's time for a switch: if ((time - lastSwitchTime) > switchInterval) { lastSwitchTime = time datasetPtr++ } drawPoints({interp: ease((time - lastSwitchTime) / switchDuration)}) }) }