---
url: /examples/basics/uniforms.md
description: Bind UI controls to shader uniforms and trigger re-renders as values change.
---

::: example-editor {deps=tweakpane@^4.0.5}

```ts
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}`;
});

```

```frag
in vec3 aPosition;
uniform vec2 uResolution;
out vec2 vUv;

void main() {
   vUv = (2.f * aPosition.xy - 1.f) * uResolution / uResolution.y;

   gl_Position = vec4(2.0f * aPosition - 1.0f, 1.0f);
}
```

```frag
varying vec2 vUv;
uniform float uScale;
uniform float uWarp;
uniform float uLacunarity;
uniform vec2 uResolution;

// hash & value noise
float hash(vec2 p) {
  return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}

float noise(vec2 p) {
  vec2 i = floor(p);
  vec2 f = fract(p);
  vec2 u = f * f * (3. - 2. * f);
  return mix(
    mix(hash(i), hash(i + vec2(1., 0.)), u.x),
    mix(hash(i + vec2(0., 1.)), hash(i + vec2(1., 1.)), u.x),
    u.y
  );
}

// fractal brownian motion
float fbm(vec2 p) {
  float value = 0.;
  float amplitude = .5;
  for (int i = 0; i < 5; i++) {
    value += amplitude * noise(p);
    p = p * uLacunarity + vec2(1.7, 9.2);
    amplitude *= .5;
  }
  return value;
}

// cosine color palette
vec3 palette(float t) {
  return vec3(.42, .36, .55) + vec3(.38, .34, .38) * cos(6.28318 * (vec3(1., .9, .7) * t + vec3(.1, .25, .55)));
}

void main() {
  vec2 uv = vUv;

  // domain-warped fbm: q warps r, r warps the final value
  vec2 q = vec2(fbm(uv * uScale), fbm(uv * uScale + vec2(5.2, 1.3)));
  vec2 r = vec2(
    fbm(uv * uScale + uWarp * q + vec2(1.7, 9.2)),
    fbm(uv * uScale + uWarp * q + vec2(8.3, 2.8))
  );
  float f = fbm(uv * uScale + uWarp * .8 * r);

  vec3 color = palette(f + .2 * length(q));
  color = mix(color, palette(f * f + .5), clamp(length(r.x), 0., 1.) * .5);

  // soft vertical falloff
  color *= smoothstep(.0, .5, gl_FragCoord.y / uResolution.y) * .6 + .5;

  gl_FragColor = vec4(color, 1.);
}

```

```css
html {
  color-scheme: light dark;
}

body {
  margin: 0;
}

canvas {
  width: 100svw;
  height: 100svh;
  display: block;
}

```

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>RadianceJS Example</title>
  </head>
  <body>
    <style>
      #renders {
        position: absolute;
        left: 1em;
        bottom: 1em;
        font-family: sans-serif;
        font-size: 1.5rem;
      }
    </style>
    <canvas id="glCanvas"></canvas>
    <div id="renders">Renders: <strong id="renderCount">0</strong></div>
    <script src="/index.ts"></script>
  </body>
</html>

```

:::
