Skip to content

Guide ​

Everything you need to know about building effects with LightScript Workshop.

Two Ways to Render ​

LightScript gives you two rendering approaches. Pick the one that fits your vision:

WebGL Effects — For the Math Wizards ​

GPU-accelerated GLSL shaders that run entirely on your graphics card. Perfect for:

  • Complex mathematical visualizations — fractals, raymarching, fluid simulations
  • High-performance rendering — 60fps even with expensive calculations
  • Shadertoy-style effects — port existing shaders with minimal changes
typescript
export class MyEffect extends WebGLEffect<MyControls> {
  // Define uniforms to pass data to your shader
  protected createUniforms() {
    return { iSpeed: { value: 1.0 }, iIntensity: { value: 0.5 } }
  }

  // Update uniforms when controls change
  protected updateUniforms(c: MyControls) {
    if (this.material) {
      this.material.uniforms.iSpeed.value = c.speed
      this.material.uniforms.iIntensity.value = c.intensity
    }
  }
}

Canvas Effects — For the Object Thinkers ​

Traditional 2D drawing with JavaScript. Great for:

  • Particle systems — hundreds of individually-tracked objects
  • Dynamic entities — things that spawn, move, and die
  • Simpler effects — when you don't need shader math
typescript
export class MyEffect extends CanvasEffect<MyControls> {
  private particles: Particle[] = []

  protected draw(time: number, deltaTime: number) {
    // Clear with fade for trails
    this.ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'
    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)

    // Draw and update each particle
    for (const p of this.particles) {
      this.ctx.fillStyle = p.color
      this.ctx.beginPath()
      this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2)
      this.ctx.fill()
      p.update(deltaTime)
    }
  }
}

The Control System ​

Controls are defined with decorators. They automatically:

  • Generate UI in the dev environment
  • Sync with SignalRGB's control panel
  • Provide type safety at compile time
typescript
@Effect({ name: 'My Effect', author: 'Me' })
export class MyEffect extends WebGLEffect<MyControls> {

  // Numeric slider — drag to adjust
  @NumberControl({
    label: 'Speed',
    min: 1,
    max: 10,
    default: 5,
    tooltip: 'Animation speed multiplier'
  })
  speed!: number

  // Boolean toggle — on/off
  @BooleanControl({
    label: 'Enable Glow',
    default: true,
    tooltip: 'Add bloom effect to bright areas'
  })
  glowEnabled!: boolean

  // Dropdown — pick from options
  @ComboboxControl({
    label: 'Palette',
    values: ['Rainbow', 'Fire', 'Ocean', 'Neon'],
    default: 'Rainbow',
    tooltip: 'Color scheme'
  })
  palette!: string

  // Hue picker — 0-360 degrees
  @HueControl({
    label: 'Base Hue',
    default: 180
  })
  baseHue!: number
}

Control Helpers ​

Raw control values often need transformation. LightScript provides helpers:

typescript
import { normalizeSpeed, normalizePercentage, comboboxValueToIndex, boolToInt } from '@lightscript/core'

// Speed: 1-10 scale → animation multiplier
// 1 = 0.2x, 5 = 1.0x, 10 = 2.0x
const speed = normalizeSpeed(window.speed ?? 5)

// Percentage: 0-100 → 0.0-1.0
const intensity = normalizePercentage(window.intensity ?? 100)

// Combobox: string label → numeric index for shader
const paletteIndex = comboboxValueToIndex(
  window.palette ?? 'Rainbow',
  ['Rainbow', 'Fire', 'Ocean', 'Neon'],
  0  // default index if not found
)

// Boolean: true/false → 1/0 for shader uniform
const glowEnabled = boolToInt(window.glowEnabled ?? true)

Audio Reactivity ​

Make your effects respond to music. LightScript provides seamless integration with SignalRGB's audio engine.

Enable Audio in Your Effect ​

Just add audioReactive: true to your WebGL effect:

typescript
constructor() {
  super({
    id: 'my-visualizer',
    name: 'My Visualizer',
    fragmentShader,
    audioReactive: true,  // ← This is all you need
  })
}

This automatically creates and updates audio uniforms every frame.

Available Audio Uniforms ​

Once enabled, these uniforms are available in your GLSL shader:

UniformTypeDescription
iAudioLevelfloatOverall volume (0-1)
iAudioBassfloatBass intensity (0-1)
iAudioMidfloatMid frequency intensity (0-1)
iAudioTreblefloatTreble intensity (0-1)
iAudioSpectrumsampler2DFull 200-band FFT as texture

Basic Audio Shader ​

glsl
uniform float iAudioLevel;
uniform float iAudioBass;
uniform float iAudioMid;
uniform float iAudioTreble;

void mainImage(out vec4 fragColor, vec2 fragCoord) {
  vec2 uv = fragCoord / iResolution.xy;

  // Pulse the whole screen with bass
  float pulse = 1.0 + iAudioBass * 0.5;

  // Color based on frequency bands
  vec3 col = vec3(iAudioBass, iAudioMid, iAudioTreble);
  col *= pulse;

  fragColor = vec4(col, 1.0);
}

Reading the Spectrum Texture ​

For per-frequency visualization, sample the spectrum texture:

glsl
uniform sampler2D iAudioSpectrum;

void mainImage(out vec4 fragColor, vec2 fragCoord) {
  vec2 uv = fragCoord / iResolution.xy;

  // Sample frequency at this x position (0-200 bands mapped to 0-1)
  float freq = texture2D(iAudioSpectrum, vec2(uv.x, 0.5)).r;

  // Draw bar visualization
  float bar = step(uv.y, freq);
  fragColor = vec4(vec3(bar), 1.0);
}

Dev Studio Audio Sources ​

In the dev environment, you can test audio reactivity using:

  • Microphone — Pick up ambient audio
  • System Audio — Capture your desktop audio (via screen share)

Click the audio panel in the dev UI to select your source.

Development Workflow ​

Start the Dev Server ​

bash
pnpm dev

Opens at localhost:4096 with:

  • Effect selector — switch between all discovered effects
  • Live controls — adjust parameters in real-time
  • Hot reloading — shader/TypeScript changes apply instantly
  • Persistence — remembers your last selected effect

Build for Production ​

bash
pnpm build:effects              # Build all effects
EFFECT=my-effect pnpm build:effects  # Build specific effect

Deploy to SignalRGB ​

Copy from dist/ to your SignalRGB effects folder:

  • Windows: ~/Documents/WhirlwindFX/Effects/
  • macOS: ~/Documents/SignalRGB/Effects/

Common Imports ​

typescript
// Everything you need from one place
import {
  // Base classes
  WebGLEffect,
  CanvasEffect,

  // Decorators
  Effect,
  NumberControl,
  BooleanControl,
  ComboboxControl,
  HueControl,
  ColorControl,

  // Helpers
  normalizeSpeed,
  normalizePercentage,
  comboboxValueToIndex,
  boolToInt,

  // Audio (for manual control)
  getAudioData,
  smoothValue,

  // Initialization
  initializeEffect,
} from '@lightscript/core'

Next Steps ​

Released under the MIT License.