Spektral WebGPU Library

repository·master·Indexed 41 days ago

https://github.com/motion-core/motion-gpu

A focused WebGPU library for fullscreen WGSL shaders, designed for shader-driven visuals, generative art, and GPU compute. It provides a framework-neutral runtime with specialized adapters for Svelte, React, and Vue, avoiding the overhead of a full 3D scene graph. Key features include validated WGSL materials, support for render, feedback, and compute passes, and a consistent core API across supported frameworks.

Tokens
13.8K
Snippets
46
Records
81
Agent score
52%

What's inside Spektral

  1. Overview of Spektral capabilities

    master

    Spektral is a focused WebGPU library designed for fullscreen WGSL shaders. It is intended for shader-driven visuals, generative art, procedural textures, post-processing, feedback effects, and GPU compute.

    Key features include:

    • Validated WGSL materials for fullscreen rendering.
    • Runtime updates for uniforms, textures, and storage buffers.
    • Support for render, feedback, and compute passes for multi-step GPU workflows.
    • Explicit render modes and a scheduler for frame-level control.
    • Source-mapped shader diagnostics and inspectable render-graph snapshots.
    • A consistent core API across Svelte, React, and Vue.
  2. Understand the Oxlint pilot linting setup

    master

    The project uses an additive linting strategy where Oxlint (v1.78.0) and oxlint-tsgolint (v7.0.2001) run before the existing ESLint (v10) gate.

    Because the pilot is additive, a successful (green) lint run provides the same level of coverage as before, but with an earlier, faster native pass over production code. This setup is designed to provide faster feedback by catching errors via Oxlint before the more intensive ESLint process begins.

  3. Follow the Host + Runtime architecture pattern

    master

    To ensure valid context usage and clean code, follow the Host + Runtime split pattern in all adapters.

    1. Host Component

    Responsible for setup and the stable rendering environment. It should:

    • Create a stable material using defineMaterial(...).
    • Render the FragCanvas component.
    • Attach configuration like passes, renderTargets, renderMode, and onError.

    2. Runtime Child Component

    Responsible for per-frame logic and interaction. It must be a child of the Host's FragCanvas to access context. It should:

    • Use useFrame(...) for per-frame updates.
    • Use useSpektral() for canvas, scheduler, or render controls.
    • Use usePointer(...) for normalized input.
    • Use useTexture(...) for texture I/O.
    • Use useSpektral().graph.getSnapshot() if inspecting the current execution plan.
  4. Note on rebundler source maps (Vite/Rollup/Next)

    master

    When using bundlers like Vite, Rollup, or Next.js, source navigation from the spektral tarball may not be zero-config because these bundlers do not preserve a node_modules input map through the final bundle by default.

    To enable this, the consumer must implement a minimal, explicit map-chain hook. This hook:

    1. Reads the adjacent .js.map from the installed spektral tarball.
    2. Returns that map unchanged to the bundler.

    Note that this hook does not create new source paths or sourcesContent; it simply preserves the published map for the bundler to use.

  5. Understand the topology resolver paired A/B evidence benchmark

    master

    The topology resolver paired A/B evidence benchmark compares two different execution paths for computing topology:

    1. Legacy renderer path: Uses public defensive pass.getResources() followed by resolveComputePassResources.
    2. Renderer-owned static-topology cache: Uses a cached approach.

    Benchmark Configuration

    • Runtime: v22.21.1
    • Execution: Uses 24 independent fresh processes (12 per arm) with balanced, alternating pairs.
    • Worker Integrity: Verified via SHA-256 (f3b620c021ee8c1bddf0a204bc86285e0387dc939eab0c4745c73bb24e8a3ba8).
    • Scope: This is a local CPU-side topology-resolution benchmark. It does not measure GPU timing or guarantee CI portability.

    Success and Regression Contracts

    • 32-pass acceptance: Requires the median cached throughput to be $\ge$ 2.0x the legacy throughput.
    • Regression Contract: A regression is defined only if all three conditions are met:
      1. The 95% bootstrap CI upper bound is below -10%.
      2. There is an absolute loss above 25,000 frames/s.
      3. The loss is larger than 3x the larger arm's Median Absolute Deviation (MAD).
    • Cached steady-state contract: After warmup, there must be zero allocations for plan, entry, read, write, layout-entry, or topology-key.
  6. WGSL Shader Entrypoint Requirements

    master

    Spektral enforces strict signatures for shader entrypoints. Do not attempt to rename these or change the parameter structure.

    Shader TypeRequired Signature
    Materialfn frag(uv: vec2f) -> vec4f
    ShaderPassfn shade(inputColor: vec4f, uv: vec2f) -> vec4f
    PingPongShaderPassfn frag(uv: vec2f) -> vec4f
    ComputePassMust contain @compute, @workgroup_size(...), and a fn compute(...) entrypoint.

    Important Notes:

    • While helpers may read from spektralFragment.uv, you must keep the explicit uv parameter in the signature.
    • In compute shaders, spektralFragment is not available; derive coordinates from global_invocation_id and an explicit domain size.
  7. Use Compute Shaders and Storage Buffers

    master

    For GPU compute tasks, use ComputePass (single-dispatch) or PingPongComputePass (iterative simulations).

    Declaration

    Declare storage buffers in defineMaterial:

    { 
      storageBuffers: { 
        name: { size, type, access? } 
      } 
    }

    Implementation Rules

    • Coordinates: Do not use spektralFragment.uv. Derive normalized coordinates manually from global_invocation_id. For a 2D texture domain:
      let uv = (vec2f(globalId.xy) + vec2f(0.5)) / vec2f(domainSize);
    • Bindings: Storage buffers are bound at group(1). Storage textures are bound at group(2).
    • Data Flow:
      • Use state.writeStorageBuffer(name, data) in useFrame to upload CPU data.
      • Use state.readStorageBuffer(name) to read GPU results asynchronously (Promise<ArrayBuffer>).
    • PingPongComputePass: The resources array must contain exactly one sampled pingPong: 'read' descriptor and one storage-write pingPong: 'write' descriptor, both referencing the same material texture with storage: true.
    let uv = (vec2f(globalId.xy) + vec2f(0.5)) / vec2f(domainSize);
  8. Requirements and use cases for Spektral

    master

    Before using Spektral, ensure your environment meets the following requirements:

    • WebGPU Support: The browser must support WebGPU.
    • Secure Context: The application must run in a secure context (HTTPS or localhost).

    When to use Spektral: Use Spektral for fullscreen shader and compute workflows where you want to own the shaders and interaction while the library handles the canvas, render loop, scheduling, and GPU resources.

    When NOT to use Spektral: If your project requires meshes, cameras, lighting, or a scene graph, a general-purpose 3D engine is a better fit, as Spektral does not include 3D tooling.

  9. Run Spektral performance benchmark suites

    master

    Spektral provides five distinct benchmark suites to answer different optimization questions. Do not combine numbers from different suites.

    Suite Overview

    • CPU core: Isolates deterministic CPU hot paths (material resolution, packing, graph planning, scheduler, etc.). Use this when changing material normalization, uniform handling, or resource caches.
    • Compute topology paired: Runs legacy defensive resource-copy paths against renderer-owned static-topology caches.
    • Browser runtime: Measures browser/runtime behavior (navigation, scheduler/render invocation rates, advance() latency) using Chromium with SwiftShader. Use this when changing lifecycle, scheduling, or adapters.
    • Hardware GPU: Exercises WGSL shaders on physical hardware using WebGPU. Measures fragment/compute ALU load, texture filtering, and pipeline compilation.
    • Real renderer: Runs the production createRenderer path on a physical GPU, measuring JS submit cost, GPU completion span, and total wall time.

    Command Reference

    Commands should be run from the repository root. To diagnose a hardware suite in a visible browser, append :headed to the command.

    # From repository root
    pnpm run perf:spektral:core             # CPU core
    pnpm run perf:spektral:compute-topology:paired # Compute topology
    pnpm run perf:spektral                    # Browser runtime
    pnpm run perf:spektral:gpu               # Hardware GPU
    pnpm run perf:spektral:renderer           # Real renderer
    
    # From packages/spektral
    pnpm run perf:core
    pnpm run perf:runtime
    pnpm run perf:gpu
    pnpm run perf:renderer
  10. Create and check Real renderer baselines

    master

    The perf:renderer suite runs the production createRenderer path on a physical GPU. It measures JS submit cost, GPU completion span, and wall time.

    Baseline Creation

    Baseline creation requires a clean worktree, at least five fresh browser processes, and an explicitly controlled power mode. It is immutable and will not overwrite existing fingerprints.

    Comparison

    Use perf:spektral:renderer:check to validate performance. The check compares the median of independent run medians and rejects baselines with fewer than five processes or incompatible hardware fingerprints.

    Note: There is no GitHub-hosted hardware gate; these checks must run on the reference Apple M4 Pro environment to ensure proper Metal/GPU access.

    # Create baseline
    SPEKTRAL_PERF_POWER_MODE=ac-high-power pnpm run perf:spektral:renderer:baseline
    
    # Check baseline
    pnpm run perf:spektral:renderer:check
  11. Implement a Spektral shader workflow

    master

    When building with Spektral, follow this structured workflow to ensure correct resource management and performance:

    1. Classify the request: Determine if your shader is Static (no updates), Animated (uniform updates in useFrame), Interactive (pointer/state-driven), Texture-driven (useTexture), a Post-processing pipeline (ShaderPass/BlitPass/CopyPass), or a Compute shader (ComputePass/PingPongComputePass).
    2. Pick layer and adapter:
      • Use framework-specific entrypoints (/svelte, /react, or /vue) for runtime usage.
      • Use core entrypoints (spektral or /core) for framework-independent tooling or low-level integrations.
    3. Design material boundary:
      • In the Material: Fragment WGSL source, helper functions, uniform/texture/storage buffer declarations, defines, includes, and color pipeline options.
      • In the Runtime (useFrame): Dynamic updates via state.setUniform(...), state.setTexture(...), state.writeStorageBuffer(...), and state.readStorageBuffer(...).
    4. Pick render cadence:
      • always: Continuous animation.
      • on-demand: Renders one frame and sleeps until invalidated. Use autoInvalidate: false + invalidation: { mode: 'on-change', token: ... } for state-driven redraws.
      • manual: Requires explicit advance() calls.
    5. Add error strategy: Always wire onError. Keep the default overlay in development unless custom UI is required.
    6. Validate: Run pnpm run check, pnpm run test, or pnpm run lint (or scoped versions like pnpm --dir packages/spektral run test).
  12. Run the paired core A/B benchmark

    master

    To execute a controlled comparison between two historical core signal sources using the paired benchmark runner, use the tsx command to run the scripts/perf/paired-core-benchmark.ts script. You must specify the number of runs and a seed for the shuffled order to ensure reproducibility.

    Results (raw process and within-process samples) are written to benchmarks/results/core-paired-ab-latest.json (which is ignored by git).

    tsx scripts/perf/paired-core-benchmark.ts --runs=20 --seed=1296523349