Skip to content

RadianceLightweight, reactive WebGL library

A toolkit for building shader-driven experiences.

✨ Simple API ​

Radiance takes care of the WebGL boilerplate, so you can focus on your shader.

import { glCanvas, fxaa, bloom, linearToneMapping } from "@radiancejs/gl";
import fragment from "./cube.frag?raw";
import "./styles.css";

glCanvas({
  canvas: "#glCanvas",
  fragment,
  postEffects: [
    fxaa(), //
    bloom({ radius: 0.5, mix: 0.8 }),
    linearToneMapping({ exposure: 1 }),
  ],
  uniforms: {
    uCubeSize: 1,
    uTime: ({ time }) => time / 500,
    uResolution: ({ canvasResolution }) => canvasResolution,
  },
  colorSpace: "display-p3",
});

🪶 Lightweight ​

The lightest way to render a simple shader, after vanilla WebGL.

vanilla WebGL
0.79 kB
68 sloc
@radiancejs/gl0.12.3
6.07 kB
6 sloc
twgl.js7.0.0
6.51 kB
39 sloc
ogl1.0.11
12.69 kB
27 sloc
three0.186.1
125.9 kB
37 sloc

weight = gzip sizesloc = source lines of code (JS) to render the shader

Updated Oct 27, 2026. See the benchmark.

↺ Reactive ​

Radiance automatically re-renders the canvas when uniforms are updated, or when the canvas is resized.

import { glCanvas } from "@radiancejs/gl";
import { Pane } from "tweakpane";
import vertex from "./vertex.frag?raw";
import fragment from "./uniforms.frag?raw";
import "./styles.css";

const { uniforms, onAfterRender } = glCanvas({
  canvas: "#glCanvas",
  vertex,
  fragment,
  uniforms: {
    uWarp: 2.8,
    uLacunarity: 3.2,
    uScale: 1.4,
    uResolution: ({ canvasResolution }) => canvasResolution,
  },
});

const pane = new Pane({ title: "Uniforms" });

// updating the uniforms object will trigger a re-render
pane.addBinding(uniforms, "uWarp", { label: "Warp", min: 1, max: 5 });
pane.addBinding(uniforms, "uLacunarity", { label: "Lacunarity", min: 2, max: 4 });
pane.addBinding(uniforms, "uScale", { label: "Scale", min: 0.5, max: 4 });

const renderCount = document.querySelector("#renderCount")!;
onAfterRender(() => {
  renderCount.firstChild!.nodeValue = `${Number(renderCount.textContent) + 1}`;
});

🛠️ Fully typed ​

Radiance provides type-safety for everything, including uniforms.

ts
const { uniforms } = glCanvas({
  canvas,
  fragment,
  uniforms: {
    uPointer: [0, 0], 
  },
});

uniforms.uPointer = 42; // Type 'number' is not assignable to type 'number[]'.

Released under the MIT License.