1.0.1 • MIT • packagerepository

Shadertoy

A TypeScript/JavaScript library for rendering Shadertoy shaders using WebGL. Built on top of @mediamonks/image-effect-renderer.

Demo

Features

  • Render Shadertoy shaders with full buffer support
  • Texture and cubemap inputs
  • Keyboard input support
  • Custom media URL mapping
  • Tree-shakable React bindings

Installation

npm install @reindernijhoff/shadertoy

Usage

Vanilla JavaScript

import { ShadertoyRenderer } from '@reindernijhoff/shadertoy';
import shader from './shader.json';

const container = document.getElementById('container');

const renderer = new ShadertoyRenderer(container, shader, {
  loop: true,
  pixelRatio: Math.min(window.devicePixelRatio, 2),
  mediaMapping: (url) => mediaMap[url], // Optional: map Shadertoy URLs to local files
});

// Control playback
renderer.play();
renderer.stop();

// Clean up
renderer.destruct();

React

import { ShadertoyRendererComponent } from '@reindernijhoff/shadertoy/react';
import shader from './shader.json';

function App() {
  return (
    <ShadertoyRendererComponent
      shader={shader}
      loop={true}
      pixelRatio={2}
      onReady={(renderer) => console.log('Ready!')}
    />
  );
}

Or use the hook for more control:

import { useShadertoyRenderer } from '@reindernijhoff/shadertoy/react';

function App() {
  const { ref, renderer, isReady } = useShadertoyRenderer({
    shader,
    loop: true,
  });

  return <div ref={ref} style={{ width: '100%', height: '100%' }} />;
}

Options

interface ShadertoyRendererOptions {
  loop?: boolean;           // Auto-play the shader (default: true)
  autoResize?: boolean;     // Auto-resize to container (default: true)
  pixelRatio?: number;      // Pixel ratio (default: devicePixelRatio)
  useSharedContext?: boolean; // Share WebGL context (default: false)
  asyncCompile?: boolean;   // Async shader compilation (default: true)
  mediaMapping?: (url: string) => string | string[] | undefined;
}

Media Mapping

Use mediaMapping to redirect Shadertoy media URLs to local or custom URLs:

const mediaMap = {
  '/media/a/texture.png': '/local/texture.png',
  '/media/a/cubemap.jpg': [  // Cubemap: 6 faces
    '/local/cubemap_0.jpg',
    '/local/cubemap_1.jpg',
    '/local/cubemap_2.jpg',
    '/local/cubemap_3.jpg',
    '/local/cubemap_4.jpg',
    '/local/cubemap_5.jpg',
  ],
};

new ShadertoyRenderer(container, shader, {
  mediaMapping: (url) => mediaMap[url],
});

Shader JSON Format

The shader JSON should be in Shadertoy's export format:

{
  "info": {
    "id": "shader_id",
    "name": "Shader Name"
  },
  "renderpass": [
    {
      "inputs": [
        {
          "filepath": "/media/a/texture.png",
          "type": "texture",
          "channel": 0,
          "sampler": { "filter": "mipmap", "wrap": "repeat" }
        }
      ],
      "code": "void mainImage(out vec4 fragColor, in vec2 fragCoord) { ... }",
      "type": "image"
    }
  ]
}

API Reference

ShadertoyRenderer

class ShadertoyRenderer {
  constructor(container: HTMLElement, shader: ShadertoyShader, options?: ShadertoyRendererOptions);

  renderer: RendererInstance;  // Underlying image-effect-renderer instance
  shader: ShadertoyShader;     // The shader data

  play(): void;      // Start rendering
  stop(): void;      // Stop rendering
  destruct(): void;  // Clean up resources
}

Development

# Install dependencies
npm install

# Build the library
npm run build

# Run vanilla example
cd examples/vanilla
npm install
npm run dev

# Run React example
cd examples/react
npm install
npm run dev

License

MIT License. See LICENSE for details.