Code

const regl = createREGL({
  extensions: ['ANGLE_instanced_arrays']
});

// Instantiate a command for drawing lines
const drawLines = reglLines(regl, {
  vert: `
    precision highp float;

    uniform float width, phase;
    uniform vec2 aspect;

    #pragma lines: attribute float theta;
    #pragma lines: position = torusKnot(theta);
    vec4 torusKnot(float theta) {
      const float p = 3.0;
      const float q = 5.0;
      float r = cos(q * theta) + 2.0;
      float phi = p * (theta - phase);
      return vec4(
        vec3(
          aspect * r * vec2(cos(phi), sin(phi)),
          -sin(q * theta)
        ) * 0.25,
        1
      );
    }

    // Return the line width from a uniform
    #pragma lines: width = getWidth();
    float getWidth() { return width; }`,
  frag: `
    precision highp float;
    uniform float width, borderWidth, pixelRatio;
    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 / pixelRatio,
            width - borderWidth + 0.5 / pixelRatio,
            sdf
          )
        ), 1);
    }`,

  // Multiply the width by the pixel ratio for consistent width
  uniforms: {
    pixelRatio: regl.context('pixelRatio'),
    aspect: ctx => ctx.viewportWidth > ctx.viewportHeight
      ? [ctx.viewportHeight / ctx.viewportWidth, 1]
      : [1, ctx.viewportWidth / ctx.viewportHeight],
    width: (ctx, props) => Math.min(ctx.viewportWidth, ctx.viewportHeight) / 30,
    borderWidth: (ctx, props) => Math.min(ctx.viewportWidth, ctx.viewportHeight) / (30 * 4),
    phase: regl.prop('phase'),
    color: regl.prop('color')
  },

  depth: {enable: true}
});

const n = 501;

// Set up the data to be drawn. Note that we preallocate buffers and don't create
// them on every draw call.
const lineData = {
  join: 'round',
  vertexCount: n + 3,
  vertexAttributes: {
    theta: regl.buffer([...Array(n + 3).keys()].map(i => i / n * Math.PI * 2))
  },
};

function draw() {
  regl.poll();
  regl.clear({color: [0.2, 0.2, 0.2, 1]});
  drawLines([
    {...lineData, phase: 0, color: [1, 0, 0.5]},
    {...lineData, phase: Math.PI, color: [0, 0.5, 1]},
  ]);
}

draw();
window.addEventListener('resize', draw);