Skip to content

Instantly share code, notes, and snippets.

@benc-uk
Last active June 22, 2025 13:11
Show Gist options
  • Save benc-uk/a69abf99ad8c9e3254b4dfd5dd5bdb65 to your computer and use it in GitHub Desktop.
Save benc-uk/a69abf99ad8c9e3254b4dfd5dd5bdb65 to your computer and use it in GitHub Desktop.
shader-demo.js
import * as twgl from 'twgl.js'
const canvas = document.querySelector('canvas')
const gl = canvas.getContext('webgl2')
const vertShader = `
#version 300 es
precision highp float;
in vec2 position;
out vec2 uv;
void main() {
gl_Position = vec4(position, 0.0, 1.0); // Set the position of the vertex
uv = position.xy * 0.5 + 0.5; // Convert from [-1, 1] to [0, 1]
}
`
// Shader that outputs pretty coloured grid
const frag1Shader = `
#version 300 es
precision highp float;
uniform vec2 window_size; // Size of the window
in vec2 uv;
out vec4 color;
const float scale = 20.0; // Scale factor for the grid
float rand(vec2 co) {
// Mad bullshit that acts like a random function
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
vec2 pixel_pos = uv * window_size; // Convert UV to pixel coordinates
vec2 grid_pos = floor(pixel_pos / scale); // Create a grid every 10 pixels
float r = rand(grid_pos + 0.0); // Random value for red
float g = rand(grid_pos + 37.0); // Random value for red
float b = rand(grid_pos + 123.0); // Random value for red
if (r > 0.5) {
r = 1.0; // Set red to 1 if condition is met
} else {
r = 0.0; // Set red to 0 otherwise
}
color = vec4(r, g, b, 1.0); // Output the color
}`
// Shader that renders the input texture to the screen
// with a slight adjustment to the red channel for visibility
const frag2Shader = `
#version 300 es
precision highp float;
in vec2 uv;
uniform sampler2D image;
out vec4 color;
void main() {
color = texture(image, uv);
color.r = color.r * 0.2 + 0.8; // Adjust red channel for visibility
}
`
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
const quadBuffers = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1],
},
})
const prog1 = twgl.createProgramInfo(gl, [vertShader, frag1Shader])
const prog2 = twgl.createProgramInfo(gl, [vertShader, frag2Shader])
// Create a framebuffer to render the first pass into
const framebuffer = twgl.createFramebufferInfo(gl, undefined, gl.canvas.width, gl.canvas.height)
// ==== Pass 1: Render the grid ====
gl.useProgram(prog1.program)
twgl.setBuffersAndAttributes(gl, prog1, quadBuffers)
twgl.setUniforms(prog1, {
window_size: [gl.canvas.width, gl.canvas.height],
})
// This tells WebGL to render to the framebuffer instead of the screen
twgl.bindFramebufferInfo(gl, framebuffer)
twgl.drawBufferInfo(gl, quadBuffers, gl.TRIANGLES)
// ==== Pass 2: Render the framebuffer to the screen ====
gl.useProgram(prog2.program)
twgl.setBuffersAndAttributes(gl, prog2, quadBuffers)
twgl.setUniforms(prog2, {
// This is the main trick: we use the texture from the framebuffer
image: framebuffer.attachments[0],
})
// This tells WebGL to render to the screen
twgl.bindFramebufferInfo(gl, null)
twgl.drawBufferInfo(gl, quadBuffers, gl.TRIANGLES)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment