Back to basics.

Click( or touch ) the square above to cycle through lines, a line strip, and a line loop.

No coordinate transformations here. All vertices are in the clip coordinate system with $x$ and $y$ values going from -1 to 1. $z$ is zero for all the vertices. Colours for the vertices are not passed in. Instead, as you can see from the vertex shader, the colour of a vertex is generated from the vertex coordinates.

According to the latest standard, a WebGL implementation is obliged to support only a line width of 1. Firefox gives me 1 on the desktop and the specified 5 on a mobile. Google Chrome gives me 5 both on the desktop and mobile.

The vertex coordinates are from an OpenGL program discussed in a draft version of a computer graphics book available here. This is mostly a WebGL version of that OpenGL program.

The code:

const vert_shader_src = `#version 300 es
precision highp float;

in vec2 line_vertex;
out vec3 colour;

void main(void) {
	gl_Position = vec4(line_vertex, 0.0, 1.0);
	colour = vec3(line_vertex.x+0.5, 1.0, line_vertex.y+0.5);
}`;

const frag_shader_src = `#version 300 es
#ifdef GL_FRAGMENT_PRECISION_HIGH
	precision highp float;
#else
	precision mediump float;
#endif

in vec3 colour;
out vec4 out_colour;

void main(void) {
	out_colour = vec4(colour, 1.0);
}`;

const g = {}; //globals

function createProgram() {
  const gl = g.gl;

  const vert_shader = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(vert_shader, vert_shader_src);
  gl.compileShader(vert_shader);

  const frag_shader = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(frag_shader, frag_shader_src);
  gl.compileShader(frag_shader);

  const prog = gl.createProgram();
  gl.attachShader(prog, vert_shader);
  gl.attachShader(prog, frag_shader);
  gl.linkProgram(prog);
  if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
    console.error(`Link failed: ${gl.getProgramInfoLog(prog)}`);
    console.error(`vs info-log: ${gl.getShaderInfoLog(vert_shader)}`);
    console.error(`fs info-log: ${gl.getShaderInfoLog(frag_shader)}`);
    throw new Error('Error creating program');
  }
  gl.deleteShader(vert_shader);
  gl.deleteShader(frag_shader);
  return prog;
}

function initGl() {
  const gl = g.gl;

  g.primitives = [gl.LINES, gl.LINE_STRIP, gl.LINE_LOOP];
  g.primitive_index = 0;
  gl.clearColor(0.2, 0.3, 0.3, 1);
  gl.enable(gl.BLEND);
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  gl.viewport(0, 0, g.canvas.width, g.canvas.height);
  gl.lineWidth(5);
}

function initProg() {
  const prog = createProgram();
  const gl = g.gl;
  gl.useProgram(prog);
  const line_vertex = gl.getAttribLocation(prog, 'line_vertex');
  const pos_buf = gl.createBuffer();
  gl.enableVertexAttribArray(line_vertex);
  gl.bindBuffer(gl.ARRAY_BUFFER, pos_buf);
  gl.vertexAttribPointer(line_vertex, 2, gl.FLOAT, false, 0, 0);

  const line_vertices = new Float32Array([
    -0.2, -0.8, 0.8, 0.2, 0.6, 0.8, -0.5, 0.6, -0.4, 0.0, -0.7, -0.3,
  ]);
  gl.bufferData(gl.ARRAY_BUFFER, line_vertices, gl.STATIC_DRAW);
}

function draw() {
  const gl = g.gl;
  gl.clear(gl.COLOR_BUFFER_BIT);
  const primitive = g.primitives[g.primitive_index];
  gl.drawArrays(primitive, 0, 6);
}

function onResize() {
  g.canvas.width = g.canvas_div.clientWidth;
  g.canvas.height = g.canvas_div.clientHeight;
  try {
    g.gl = g.canvas.getContext('webgl2', { alpha: false, depth: false });
    if (!g.gl) {
      throw new Error('Browser does not support WebGL2');
    }
  } catch (e) {
    g.canvas_div.innerHTML = `<p>${e.message}</p>`;
    return;
  }
  try {
    initGl();
    initProg();
  } catch (e) {
    g.canvas_div.innerHTML = `<p>${e.message}</p>`;
    return;
  }
  draw();
}

function onClick() {
  g.primitive_index = (g.primitive_index + 1) % g.primitives.length;
  draw();
}

function init() {
  g.canvas_div = document.querySelector('#canvas_div');
  g.canvas = document.querySelector('canvas');
  g.canvas.addEventListener('click', onClick);
  onResize();
}

window.addEventListener('load', init);
window.addEventListener('resize', onResize, false);
window.addEventListener('orientationchange', onResize, false);