Code
const regl = createREGL({
extensions: ['ANGLE_instanced_arrays'],
attributes: {antialias: false}
});
// Instantiate a command for drawing lines
const drawLines = reglLines(regl, {
vert: `
precision highp float;
uniform float width, time, phase;
#pragma lines: attribute float x;
#pragma lines: position = getPosition(x);
vec4 getPosition(float x) {
float theta = 3.141 * (6.0 * x + time) - phase;
return vec4(
0.5 * vec3(
cos(theta),
(x * 2.0 - 1.0) * 1.5,
sin(theta)
),
1
);
}
// Return the line width from a uniorm
#pragma lines: width = getWidth();
float getWidth() {
return width;
}`,
frag: `
precision highp float;
uniform float width, borderWidth;
uniform vec3 color;
varying vec3 lineCoord;
void main () {
// Convert the line coord into an SDF
float sdf = length(lineCoord.xy) * width;
// Apply a border with 1px transition
gl_FragColor = vec4(
mix(color, vec3(1), smoothstep(width - borderWidth - 0.5, width - borderWidth + 0.5, sdf)),
1);
}`,
// Multiply the width by the pixel ratio for consistent width
uniforms: {
width: (ctx, props) => ctx.pixelRatio * props.width,
borderWidth: (ctx, props) => ctx.pixelRatio * props.borderWidth,
time: regl.context('time'),
phase: regl.prop('phase'),
color: regl.prop('color')
},
depth: {enable: true}
});
const n = 101;
const x = [...Array(n).keys()].map(i => i / (n - 1));
// Set up the data to be drawn. Note that we preallocate buffers and don't create
// them on every draw call.
const lineData = {
width: 30,
join: 'round',
cap: 'round',
joinResolution: 1,
vertexCount: x.length,
vertexAttributes: { x: regl.buffer(x) },
endpointCount: 2,
endpointAttributes: { x: regl.buffer([x.slice(0, 3), x.slice(-3).reverse()]) },
borderWidth: 5
};
regl.frame(() => {
regl.poll();
regl.clear({color: [0.2, 0.2, 0.2, 1]});
drawLines([
{...lineData, phase: 0, color: [0.5, 1, 0]},
{...lineData, phase: Math.PI / 2, color: [0, 0.5, 1]},
{...lineData, phase: Math.PI, color: [1, 0, 0.5]}
]);
});