A TypeScript DSL for building shader programs. Define a node graph in TypeScript and compile it to GLSL (WebGL 2), WGSL (WebGPU), JavaScript, or WebAssembly (two CPU targets for screen picking and other per-pixel host-side work).
import { Fn, float, vec4, uniform, compileGLSL, compileWGSL, compileJSRoutine, compileWasmRoutine } from "rmsl";
let prog = Fn(() => {
let color = uniform("vec4");
let brightness = float(0.5).toVar();
return color.mul(brightness).toVar();
});
let glsl = compileGLSL(prog());
let wgsl = compileWGSL(prog());
let js = compileJSRoutine(() => prog()); // fn(ctx) -> color, run on the CPU
let wasm = compileWasmRoutine(() => prog(), { name: "main", params: [] }); // same contract, real WASM module- Type-safe - TypeScript types for all shader types: float/int/uint/bool, vec2-4, ivec2-4, uvec2-4, mat2-4, and float/integer samplers (sampler2D/3D/Cube, isampler2D/3D/Cube, usampler2D/3D/Cube)
- Four backends - Compile to GLSL ES 3.0, WGSL, JavaScript, or WebAssembly from the same node graph
- CPU / JS target -
compileJSRoutine/compileJSFnturn anFninto a callable that runs on the CPU, one fragment at a time — for screen picking from a ray-marched scene without a GPU round-trip. Per-call evaluation allocates nothing (hoisted scratch slots + out-parameter vector helpers) - CPU / WASM target -
compileWasmRoutine/compileWasmFncompile the sameFnto a real WebAssembly module instead — reali32/f64types, noeval, and a.draw(ctx, width, height)that evaluates a whole pixel grid through one exported WASM function rather than one host call per pixel. Satisfies the sameCpuRoutineinterface ascompileJSRoutine's result, so code can pick between them without knowing which it got - CPU vertex/fragment rasterizer -
compileJS/compileWasmlink a compiled vertex/fragmentFnpair against a generic triangle rasterizer (near-plane clipping, a LEQUAL depth test) instead of a GPU —createJs/createWasmwrap the result in the sameAdapterinterfacecreateGlsl/createWgsluse - Post-processing effects -
import { fxaa, gaussianBlur, crt, ... } from "@random-mesh/rmsl/effects"provides a port of three.js'sexamples/jsm/tsl/displaycolor effects as pure node graphs (no renderer inside — you draw the quad), plusfragCoord()/screenUV()/textureLoad()/textureSize()and friends in the core DSL - Scene graph & node materials -
import { Scene, Mesh, WebGLRenderer, ... } from "@random-mesh/rmsl/scene"provides three.js-style scene-graph objects and node-based materials — a material is an RMSL node graph (colorNode,roughnessNode,fragmentNode, ...) compiled by the same DSL compiler, with WebGL2 and WebGPU renderers that bind geometry, upload uniforms and draw - Shader tests without a GPU -
import { evaluate, render } from "@random-mesh/rmsl/test"runs a shader graph on the CPU and hands back values or a grid of fragments, so a colour ramp, a distance field or a lighting term is asserted on in a plain unit test — no browser, no canvas, no pixel readback.fromProgram/fromPassdo the same for a built scene material or one pass of an effect, addressed by the names they use - Vite plugins -
import { precompileShaders, precompileJS, precompileWasm } from "@random-mesh/rmsl/vite"compiles the node graph at build time, so the browser ships plain GLSL/WGSL strings, JS callables, or a real.wasmasset instead of rmsl itself - Casts & conversions -
uint(),ivec3(vec3), and chained.toInt()/.toVec3()/.toUVec4()/… for any type - Constant folding - Math on literal values is evaluated at compile time
- Control flow -
If/ElseIf/Else,Switch/Case/Default,For,While,Loop,Break,Continue,Return,Discard— matching TSL - TSL-compatible API - free functions like
mul(a, b),sin(x),mix(a, b, t),bool(), and thePI/TWO_PI/EPSILON/… constants, so a shader written againstthree/tslmigrates by changing its import - Swizzles -
.xyz,.rgba,.stpq,.xy, etc. on vec3/vec4, ivecN and uvecN (read and write) - Integer textures - isampler*/usampler* sample to ivec4/uvec4 via unfiltered texelFetch/textureLoad
- Vertex/fragment - Separate vertex and fragment compilation with proper I/O
- Built-in outputs -
output(),builtinPosition(),varying(),attribute(),uniform()
- Getting Started - Quick setup and hello world
- API Reference - Full type system, constructors, and operations
- TSL Migration - Porting a Three.js TSL shader to RMSL
- Compilation - GLSL/WGSL output, type mappings, binding model, and the JS CPU target
- WASM Target -
compileWasmRoutine, the second CPU target — a real WebAssembly module instead of JS source - Effects - Post-processing effects ported from three.js TSL (
@random-mesh/rmsl/effects) - Scene Graph - three.js-style scene objects and node-based materials (
@random-mesh/rmsl/scene) - Testing - Running shader logic on the CPU in unit tests (
@random-mesh/rmsl/test) - Vite Plugins - Precompiling shaders and CPU callables at build time so rmsl is never shipped
- Contributing - Test setup, and how to add an operation or a shader type