shadertown

Svelte shader backgrounds, no wrapper required

React is a binding here, not the foundation. createShaderRenderer is a plain function that takes a canvas, so a Svelte animated background is onMount, one reactive statement, and nothing else.

A plain function, mounted the Svelte way

onMount may return a cleanup function, which is exactly where destroy() belongs. Build the renderer once with the canvas Svelte bound for you, and the shader background is running.

Reactive params, no recompile

setParams never recompiles a pipeline, so a reactive statement can push component state at the renderer as fast as it changes — bind a range input to speed and drag it live.

SvelteKit and the server

The renderer is browser-only, and onMount only runs in the browser — so SvelteKit's server rendering needs no guard at all. The framework docs show the same shape for Astro and plain JavaScript.

Hero.svelte
<script lang="ts">
  import { onMount } from "svelte";
  import { createShaderRenderer, type ShaderRenderer } from "shadertown";

  let canvas: HTMLCanvasElement;
  let renderer: ShaderRenderer | undefined;
  export let speed = 0.4;

  onMount(() => {
    renderer = createShaderRenderer(canvas, {
      shader: "aurora",
      params: { speed },
    });
    return () => renderer?.destroy();
  });

  $: renderer?.setParams({ speed });
</script>

<canvas bind:this={canvas}></canvas>

Tune first, mount second

Every shader runs live in the catalogue with every slider free — the params you settle on go straight into createShaderRenderer.