uniform float uSpeed; // 0..5 = 1.5 a slider from 0 to 5, starting at 1.5
uniform int uCount; // 1..12 = 6 whole numbers only
uniform vec3 uTint; // color = #ff8800 a colour picker
uniform vec2 uCentre; // -1..1 = 0.2, 0 one slider per component
uniform bool uGrid; // = true a checkbox
uniform float uAngle; // min 0 max 2pi step 0.01 default 1
Shader playground
Write GLSL fragment shaders and watch them run as you type — WebGL2 with a WebGL1 fallback, compile errors on their lines, Shadertoy's iTime/iResolution/iMouse/iFrame, sliders and colour pickers for your own uniforms, pause, step and scrub, PNG and WebM export, .frag export for Shadertoy, glslViewer and other hosts, share links, and nine examples with lessons from UV gradients to raymarching and a Boing ball.
Added · updated 27 October 2026
How to use it
A fragment shader is a small program that runs once for every pixel, all at the same time, on the graphics card. This page compiles the one in the editor every time you stop typing and runs it every frame on the canvas. If it doesn’t compile, the errors are marked on their lines and the last version that worked keeps running.
It speaks Shadertoy’s dialect: write void mainImage(out vec4 fragColor, in vec2 fragCoord) and use
iResolution, iTime, iTimeDelta, iFrame, iMouse and iDate, and most single-pass
Shadertoy shaders that don’t read textures paste straight in.
How to use it
Examples load with one click, each with a lesson beside the canvas: UV coordinates and gradients,
shapes with signed distance functions, smoothstep and antialiasing, polar coordinates, value
noise and fbm with domain warping, domain repetition, raymarching a sphere with soft shadows, the
demo-scene plasma, and a checkered Boing ball bouncing over a purple grid. The Lessons tab has all
the explanations in one place. I wrote all of them for this page.
Your own uniforms become controls. Declare a uniform with a comment and it gets a control under the canvas:
Time. Pause and play (or Space on the canvas), step one frame, go back to 0, or drag the time
slider to scrub; the speed menu runs time from a quarter to four times as fast. Drag on the canvas
to set iMouse: xy while the button is down, zw where it went down, negative after you let go,
as on Shadertoy.
Output. Save PNG saves the frame at the canvas’s full resolution. Record captures a few seconds of the canvas as a WebM video (MP4 in Safari). ⛶ Full screen gives the canvas the whole screen, with the editor hidden behind ◫ Panels. The resolution menu renders at half or a quarter of the canvas’s pixels when a shader is too slow.
Share link (at the top) puts the shader’s source and your slider settings into the link, so the person who opens it sees the same thing.
Taking a shader to Shadertoy and other tools
Copy for Shadertoy puts a version on the clipboard that you can paste over everything in the Image tab at shadertoy.com/new and run. Download ▾ saves the same thing as a file, or one of the other forms:
- Shadertoy .frag
-
Just
mainImage()and the functions it calls: no#version, noprecisionline, no declarations ofiTime,iResolutionand the rest, because Shadertoy adds those itself. The Shader Toy extension for VS Code opens the same file. - Standalone GLSL ES 3.00 .frag
-
#version 300 es, the six uniforms declared, and amain()that callsmainImage(). For three.js, your own WebGL code, or anything else that takes a fragment shader and can setiTimeandiResolution. - glslViewer .frag
-
For glslViewer (
glslViewer shader.frag): it declares glslViewer’su_time,u_resolution,u_mouse,u_delta,u_dateandu_frameand#define`s the Shadertoy names to them. glslViewer has no mouse-button state, so `iMouse.zwstays 0. - Playground source (.glsl)
-
Exactly what is in the editor.
Shadertoy has no sliders, so in the exported files each slider uniform becomes a constant set to where the slider is now, with its comment kept:
uniform float uSpeed; // 0..5 = 1.5 in the playground, slider at 3.2
const float uSpeed = 3.2; // 0..5 = 1.5 in the exported file
A shader written with its own void main() is converted to mainImage(): gl_FragColor (or its
single out vec4) becomes fragColor and gl_FragCoord.xy becomes fragCoord. Uniforms named
the glslViewer / Book of Shaders way (u_time, u_resolution, u_mouse) or the GLSL Sandbox way
(time, resolution, mouse) are #define`d to the Shadertoy ones. When a conversion would
change what the shader does — it writes `gl_FragData, reads a varying, has more than one output,
or writes the output from another function — nothing is downloaded and the page says why. Every
file is compiled in your browser before it is saved (the Shadertoy one inside a copy of
Shadertoy’s own wrapper), so a file that would not compile is not saved either.
The header comment names the shader, links back to it (to the example, or a share link if the shader is short enough to fit in one), and says the licence: my examples are MIT, so use them as you like; for your own shader it’s your choice — Shadertoy assumes CC BY-NC-SA 3.0 unless the code says otherwise.
Open… reads all of these back. A Shadertoy file opens as it is (a #version or precision line
is removed, since the playground adds its own); a file saved from here gets its sliders back, set
to the values it was saved with; the VS Code extension’s #iUniform lines become sliders and its
#iChannel lines are commented out (no textures here); and a Book of Shaders or GLSL Sandbox
shader gets the time, resolution and mouse it expects.
A source with its own void main() is compiled as written: GLSL ES 1.00 (with gl_FragColor) works
everywhere; #version 300 es needs WebGL2. The playground fills in the uniforms it knows about if
you declare them.
Browser support
Every current browser has WebGL2: Chrome, Edge, Firefox and Safari 15 and later, on desktop and
phones. Where only WebGL1 is available, the same shaders are compiled as GLSL ES 1.00 (the examples
avoid anything newer). Recording needs MediaRecorder on a canvas stream, which all of them have.
Not supported: textures and iChannel0–3, multi-pass buffers, sound and cube-map shaders.
The exported files are compiled with WebGL2 before they are saved; in a browser with only WebGL1
they are saved without that check.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.