Basic

Interactive dot grid with cursor tracking and animated custom uniforms.

View live demoRaw .ts
/**
 * Interactive dot grid with cursor tracking. Uses built-in uniforms (u_time, u_cursor, u_click)
 * and custom uniforms with dynamic updates.
 */
import ShaderPad from 'shaderpad';
import autosize from 'shaderpad/plugins/autosize';
import { createFullscreenCanvas } from 'shaderpad/util';

import type { ExampleContext } from '@/examples/runtime';

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

// Built-in variables.
in vec2 v_uv;
uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_cursor; // [cursorX, cursorY]
uniform vec3 u_click; // [clickX, clickY, isClicked]

// Custom variables.
uniform vec3 u_cursorColor;

out vec4 outColor;

void main() {
  vec2 uv = v_uv * u_resolution;
  vec2 dotGrid = mod(uv, 50.) - 25.;
  float dotDist = length(dotGrid);
  float dot = step(dotDist, 5.);

  vec2 clickPos = u_click.xy;
  float isClicked = u_click.z;

  float cursorDist = distance(uv, u_cursor * u_resolution);
  float clickDist = distance(uv, clickPos * u_resolution);

  float cursorRadius = 25. + sin(u_time * 5.) * 5. + isClicked * 15.;
  float cursor = step(cursorDist, cursorRadius);
  float click = step(clickDist, 15.);

  vec3 color = mix(vec3(0., 0., 1.), vec3(1.), dot);
  color = mix(color, u_cursorColor, cursor);
  color = mix(color, vec3(1., 1., 1.), click);

  outColor = vec4(color, 1.);
}`;

export async function init({ mount }: ExampleContext) {
	// Initialize the shader.
	const canvas = createFullscreenCanvas(mount);
	const shader = new ShaderPad(fragmentShaderSrc, {
		plugins: [autosize()],
		canvas,
		cursorTarget: window,
	});

	// Add your own custom uniforms.
	const getColor = (time: number) =>
		[time, time + (Math.PI * 2) / 3, time + (Math.PI * 4) / 3].map(x => 1 + Math.sin(x) / 2);
	shader.initializeUniform('u_cursorColor', 'float', getColor(0));

	// Start the render loop.
	shader.play(time => {
		shader!.updateUniforms({ u_cursorColor: getColor(time) });
	});

	return () => {
		shader.destroy();
		canvas.remove();
	};
}