Wyn Playground

Shader development

Write an ordinary Wyn function. The playground supplies live inputs, builds the fullscreen graphics pipeline, compiles it to WGSL, and runs it with WebGPU.

The main_image contract

Every playground shader defines one function named main_image. Each invocation computes the color of one pixel and returns that color as a four-component RGBA vector. This is the same role played by a fragment shader in WGSL or GLSL: the GPU runs it independently across all fragments covered by the image.

Playground shader source is intentionally not a complete Wyn program. The playground combines your function with a prelude framework that calls main_image for every pixel in the rendered image. You write the per-pixel computation while the framework supplies the graphics-pipeline entry and resource plumbing.

The playground can also provide live metadata such as the framebuffer size, elapsed time, frame number, mouse state, and current pixel coordinate. Request a value by adding its recognized name and exact type to main_image. Parameters may appear in any order, and you only declare the values your shader needs.

-- A playground image shader. Choose any supported inputs by name; the
-- playground supplies them and builds the graphics pipeline around this code.

def main_image(resolution: vec3f32,
               time: f32,
               frag_coord: vec2f32) vec4f32 =
  let uv = frag_coord / resolution.xy in
  let phase = time in
  let r = 0.5 + 0.5 * f32.cos(phase + uv.x * 3.0) in
  let g = 0.5 + 0.5 * f32.cos(phase + uv.y * 3.0 + 2.0) in
  let b = 0.5 + 0.5 * f32.cos(phase + (uv.x + uv.y) * 1.5 + 4.0) in
  @[r, g, b, 1.0]

See the specification chapters on functions and values, expressions, and vector types.

Available inputs

These are the names and types the playground recognizes. It matches the parameters in your main_image signature against this table and connects each requested value to the corresponding browser or GPU state. A misspelled name or incorrect type is reported as a playground source error.

NameTypeValue
resolutionvec3f32Framebuffer width, height, and pixel aspect ratio (currently 1.0).
timef32Seconds elapsed since the shader started, excluding paused time.
time_deltaf32Seconds since the preceding rendered frame.
frame_ratef32Current frame rate, calculated as 1 / time_delta.
framei32Rendered frame number, beginning at zero.
channel_time[4]f32Channel playback times. These are zero until channels are supported.
channel_resolution[4]vec3f32Channel dimensions. These are zero until channels are supported.
mousevec4f32Pointer position in xy and click position/state in zw, in framebuffer pixels.
datevec4f32Local year, month, day, and seconds since midnight.
sample_ratef32Audio sample rate; currently 44,100 Hz.
frag_coordvec2f32The current pixel position in framebuffer coordinates.

Coordinates and mouse state

frag_coord and mouse coordinates use framebuffer pixels with the origin at the bottom left. While the primary pointer button is held,mouse.xy is the current position and mouse.zwis the positive click position. After release, zw becomes negative. All four components are zero before the first click.

Compile, inspect, and debug

  • Choose Compile & Run or press Ctrl/⌘+Enter.
  • Compiler diagnostics refer to your editor lines; framework locations are not exposed as user source.
  • Pause and restart the animation with the preview controls, or use fullscreen for a larger framebuffer.
  • Open Pipeline to inspect generated entry points and resource bindings.
  • Use the TLC, MIR, and WGSL output tabs to see the compiler’s intermediate and final output.

Rendering requires a browser with WebGPU support. Compilation can still report source errors before a render pipeline is created.

Save and share

Anyone can edit and run a shader locally in the page. Sign in with GitHub to save. A saved shader stores your main_image source, title, and a preview thumbnail, then receives a public URL. Only the owner can update it; viewing a shared shader does not require an account.