Shadertoy uniforms are the built-in inputs your shader receives from the renderer. If you understand what each uniform represents, especially iChannel0..3, iDate, and iFrame, you can read existing shaders faster, port them with fewer bugs, and avoid the timing mistakes that break animations, feedback buffers, and texture-driven effects.
This guide explains the Shadertoy uniforms that matter most in practice. It focuses on how they behave, when to use them, and the coding patterns that show up repeatedly in image shaders, buffer passes, and texture-based effects.
What are Shadertoy uniforms?
Shadertoy uniforms are read-only values passed into your fragment shader by the host environment. They expose information such as resolution, elapsed time, frame count, mouse state, date, channel inputs, and per-channel metadata. In practical terms, they are how a shader learns about the outside world without hard-coding values.
The most common built-ins include resolution, time, mouse input, date, audio sample rate, and up to four input channels. You do not create these uniforms inside Shadertoy itself. You reference them, and the platform fills them in each frame.
uniform vec3 iResolution;
uniform float iTime;
uniform float iTimeDelta;
uniform int iFrame;
uniform vec4 iMouse;
uniform vec4 iDate;
uniform vec3 iChannelResolution[4];
uniform float iChannelTime[4];
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform sampler2D iChannel3;
Which Shadertoy uniforms matter most day to day?
| Uniform | Type | What it gives you | Typical use |
|---|---|---|---|
iResolution | vec3 | Viewport size in pixels | Normalised UVs, aspect correction |
iTime | float | Playback time in seconds | Smooth animation |
iTimeDelta | float | Time since previous frame | Stable simulation updates |
iFrame | int | Current frame number | Initialisation, stepping, frame gating |
iMouse | vec4 | Current and click mouse state | Interaction and dragging |
iDate | vec4 | Year, month, day, seconds since midnight | Calendars, seeding, day-night logic |
iChannel0..3 | sampler2D or cubemap | Up to four bound inputs | Textures, buffers, video, sound, cubemaps |
iChannelResolution | vec3[4] | Resolution of each channel | Correct sampling and scaling |
iChannelTime | float[4] | Playback time for each channel | Video and audio sync |
How do iChannel0, iChannel1, iChannel2, and iChannel3 work?
iChannel0..3 are Shadertoy’s input slots. Each one can point to a texture, another buffer, a cubemap, a video source, keyboard data, or an audio input depending on how the shader is configured. They are the main reason many Shadertoy effects are modular: one pass generates data, another pass reads it through an iChannel.
The important detail is that an iChannel is just a binding slot. iChannel0 does not always mean “the main texture”. It means “whatever input is attached to slot 0 in this shader pass”. When porting or reading code, always inspect the pass configuration before assuming what a channel contains.
Basic texture sampling pattern
vec2 uv = fragCoord / iResolution.xy;
vec3 tex = texture(iChannel0, uv).rgb;
fragColor = vec4(tex, 1.0);
This is the simplest pattern. The shader uses screen-space UVs and samples iChannel0 as if it were a full-screen image. It is common in post-processing shaders, distortion effects, blur passes, and simple compositing.
Resolution-aware sampling pattern
vec2 uv = fragCoord / iChannelResolution[0].xy;
vec3 tex = texture(iChannel0, uv).rgb;
Use this when the input texture does not match the screen resolution. This matters in buffer chains, low-resolution simulation buffers, and video inputs. Many broken ports come from normalising against iResolution when the correct domain is the channel’s own size.
Feedback buffer pattern
vec2 uv = fragCoord / iResolution.xy;
vec3 prev = texture(iChannel0, uv).rgb;
vec3 next = mix(prev, vec3(0.0), 0.02);
fragColor = vec4(next, 1.0);
This pattern is used in Buffer A or Buffer B passes where iChannel0 points to a previous frame. It is the basis for trails, reaction-diffusion, temporal accumulation, cellular automata, and fluid-like experiments. The key point is that the channel holds state from an earlier pass or earlier frame, not a static image.
Multi-input compositing pattern
vec2 uv = fragCoord / iResolution.xy;
vec3 base = texture(iChannel0, uv).rgb;
vec3 detail = texture(iChannel1, uv * 2.0).rgb;
vec3 colour = base * 0.7 + detail * 0.3;
fragColor = vec4(colour, 1.0);
Using multiple channels is common when a shader blends a source image with noise, a lookup texture, a mask, or another buffer. In practice, iChannel0 is often the primary signal and the remaining channels act as supporting inputs.
What does iDate contain in Shadertoy?
iDate is a vec4 that stores calendar-style values: year, month, day, and seconds since midnight. The first three components are straightforward. The fourth is the one that matters most in shader logic because it gives you a time value tied to the real clock rather than playback time.
Unlike iTime, which starts when the shader starts playing, iDate.w is anchored to the current day. That makes it useful for date-based variation, daily seeds, clock displays, and effects that should differ from one run to the next without needing user interaction.
Common iDate usage
float secondsToday = iDate.w;
float hours = floor(secondsToday / 3600.0);
float minutes = floor(mod(secondsToday, 3600.0) / 60.0);
float seconds = mod(secondsToday, 60.0);
This pattern is common in clock shaders and daily animation cycles. It is also a practical way to build deterministic variation that changes each day without changing every frame in the same way that iTime does.
Using iDate as a seed
float dailySeed = fract(sin(dot(iDate.xy, vec2(12.9898, 78.233))) * 43758.5453);
This is a typical shader trick. The point is not the exact hash, but the idea: use calendar values to derive a repeatable number that changes when the date changes. That is useful for generative backgrounds, daily palettes, or small visual differences across sessions.
Important caveat with iDate.w
iDate.w grows through the day, so the fractional precision becomes less useful late in the day if you try to treat it as a high-quality sub-second random source. Use it for coarse seeding or real-time clocks, not as a perfect substitute for a dedicated random input.
What does iFrame do, and when should you use it?
iFrame is the current playback frame number. It increases by one for each rendered frame, which makes it useful when you need discrete steps rather than smooth elapsed time. That is the main difference from iTime: frame count is count-based, while playback time is continuous.
In practice, iFrame is best for initialisation, ping-pong logic, stepping simulations, or triggering behaviour every N frames. It is not the best driver for smooth motion because different devices render at different frame rates, and frame pacing is not perfectly regular.
Initialisation on the first frame
if (iFrame == 0) {
fragColor = vec4(0.0);
return;
}
This is one of the most useful Shadertoy patterns. When you need a buffer to start from a known state, checking iFrame == 0 is more reliable than checking iTime. It keeps your setup logic tied to the actual first frame rather than an approximate moment in playback time.
Periodic switching pattern
bool evenFrame = (iFrame % 2) == 0;
vec3 colour = evenFrame ? vec3(1.0, 0.2, 0.1) : vec3(0.1, 0.2, 1.0);
fragColor = vec4(colour, 1.0);
This approach is useful in debug shaders, ping-pong buffers, checker-step simulations, and temporal experiments where exact alternation matters more than smooth interpolation.
When not to use iFrame
Do not use iFrame for camera motion, oscillation speed, or anything meant to feel smooth and time-based. If one system renders at 30 FPS and another at 120 FPS, a frame-driven animation will move at different perceived speeds unless you correct for that manually.
Should you use iTime or iFrame?
Use iTime for smooth motion. Use iFrame for discrete control. That is the decision that solves most confusion around Shadertoy timing.
If you are animating a raymarch camera, a pulse, a scroll, or any effect that should feel consistent in real time, iTime is usually correct. If you are bootstrapping a feedback buffer, alternating states, or stepping a simulation grid, iFrame is usually correct.
| Goal | Prefer | Why |
|---|---|---|
| Smooth animation | iTime | Consistent in seconds, not frames |
| Simulation delta | iTimeDelta | Uses real frame duration |
| First-frame setup | iFrame | Exact first-frame check |
| Every-N-frame events | iFrame | Discrete counting |
| Clock or day-based variation | iDate | Bound to real calendar time |
| Video sync | iChannelTime[n] | Tracks the media channel |
Common Shadertoy uniform patterns worth learning
1. Normalise by screen size
vec2 uv = fragCoord / iResolution.xy;
This is the default full-screen pattern. It converts pixel coordinates into a 0 to 1 range so the shader behaves predictably across resolutions.
2. Centre the coordinate system
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
This is the standard pattern for signed coordinates centred on the middle of the viewport. It is especially common in raymarching and signed distance field work.
3. Aspect-correct circles and distances
vec2 uv = fragCoord / iResolution.xy;
vec2 p = uv - 0.5;
p.x *= iResolution.x / iResolution.y;
If you skip aspect correction, circles become ellipses and radial effects distort on non-square viewports.
4. Sample channels in their own coordinate space
vec2 tuv = fragCoord / iChannelResolution[0].xy;
vec4 data = texture(iChannel0, tuv);
This matters when a buffer or texture is intentionally lower resolution than the screen. It is one of the most common hidden bugs in ports.
5. Gate one-time logic with iFrame
if (iFrame < 2) {
// warm-up or initialise
}
Some multi-pass shaders need more than one startup frame before all buffers contain valid data. This pattern is common in feedback-heavy setups.
6. Use iChannelTime for media-driven effects
float t = iChannelTime[0];
If the channel is a video or audio-linked input, this is usually the safer sync source than iTime. It keeps the effect aligned with the actual media playback.
Common mistakes with Shadertoy uniforms
- Assuming
iChannel0is always a 2D image. It may be a buffer, cubemap, video, or something else. - Sampling a channel using
iResolutioninstead ofiChannelResolution[n]. - Driving smooth animation with
iFrameand then wondering why speed changes across devices. - Using
iTimefor first-frame initialisation instead of checkingiFrame == 0. - Treating
iDate.was a perfect high-precision random source. - Porting a shader and forgetting to bind all required channels and their metadata.
- Ignoring coordinate conventions when moving a shader into WebGL, Three.js, Unity, Godot, or a custom player.
Operational checklist for reading or porting a Shadertoy shader
- List every built-in uniform the shader references before changing anything.
- Identify what each
iChannelis actually bound to. - Check whether sampling should use screen resolution or channel resolution.
- Separate smooth animation logic from discrete frame logic.
- Use
iFramefor startup and state transitions, not motion speed. - Use
iChannelTimewhen media sync matters. - Test at more than one resolution and frame rate.
Shadertoy uniforms example combining iChannel0, iDate, and iFrame
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
// Sample primary input
vec3 base = texture(iChannel0, uv).rgb;
// Daily variation from date
float daily = fract(sin(dot(iDate.xyz, vec3(12.9898, 78.233, 37.719))) * 43758.5453);
// Frame-based pulse every 30 frames
float pulse = ((iFrame % 30) == 0) ? 1.0 : 0.0;
vec3 tint = mix(vec3(1.0), vec3(0.8 + 0.2 * daily, 0.9, 1.0), 0.35);
vec3 colour = base * tint + pulse * 0.1;
fragColor = vec4(colour, 1.0);
}
This small example shows the separation of concerns clearly. iChannel0 provides source data, iDate introduces a stable daily variation, and iFrame handles a discrete periodic event. That is exactly how these uniforms are most useful in real shaders.
Once you understand what each Shadertoy uniform is doing, the next bottleneck is usually debugging the GLSL itself. Modern AI tools for coding can help explain shader errors, refactor repeated math, and turn small visual ideas into cleaner fragment shader experiments.
Where to learn the official Shadertoy uniform model
For the original platform reference and examples, use the official Shadertoy site. It is still the best place to inspect real shaders, see how passes are wired, and verify what inputs a shader expects.
FAQ
What are the main Shadertoy uniforms?
The main Shadertoy uniforms are iResolution, iTime, iTimeDelta, iFrame, iMouse, iDate, iChannel0..3, iChannelResolution, and iChannelTime. Together they describe the viewport, timing, input state, and bound resources.
What is iChannel0 in Shadertoy?
iChannel0 is the first input slot for the current shader pass. It can hold a texture, buffer, cubemap, video, audio-related data, or another supported source depending on the pass configuration.
What is iDate in Shadertoy?
iDate is a four-component vector containing year, month, day, and seconds since midnight. It is useful for clocks, calendar-based logic, and stable date-driven variation.
What is iFrame in Shadertoy?
iFrame is the integer frame counter for shader playback. It is best used for initialisation, frame stepping, and discrete logic rather than smooth time-based animation.
Should I animate with iFrame or iTime?
Use iTime for smooth motion and iFrame for discrete control. In practice, that one rule prevents most timing bugs in Shadertoy code.
Why does a Shadertoy port often break around iChannel uniforms?
Most ports fail because they do not bind the correct channel input, do not provide matching channel resolution metadata, or assume the channel uses screen-space coordinates when it does not.
