Shadertoy Uniforms Explained: iChannel0..3, iDate, iFrame, and Common Patterns

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?

UniformTypeWhat it gives youTypical use
iResolutionvec3Viewport size in pixelsNormalised UVs, aspect correction
iTimefloatPlayback time in secondsSmooth animation
iTimeDeltafloatTime since previous frameStable simulation updates
iFrameintCurrent frame numberInitialisation, stepping, frame gating
iMousevec4Current and click mouse stateInteraction and dragging
iDatevec4Year, month, day, seconds since midnightCalendars, seeding, day-night logic
iChannel0..3sampler2D or cubemapUp to four bound inputsTextures, buffers, video, sound, cubemaps
iChannelResolutionvec3[4]Resolution of each channelCorrect sampling and scaling
iChannelTimefloat[4]Playback time for each channelVideo 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.

GoalPreferWhy
Smooth animationiTimeConsistent in seconds, not frames
Simulation deltaiTimeDeltaUses real frame duration
First-frame setupiFrameExact first-frame check
Every-N-frame eventsiFrameDiscrete counting
Clock or day-based variationiDateBound to real calendar time
Video synciChannelTime[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 iChannel0 is always a 2D image. It may be a buffer, cubemap, video, or something else.
  • Sampling a channel using iResolution instead of iChannelResolution[n].
  • Driving smooth animation with iFrame and then wondering why speed changes across devices.
  • Using iTime for first-frame initialisation instead of checking iFrame == 0.
  • Treating iDate.w as 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 iChannel is actually bound to.
  • Check whether sampling should use screen resolution or channel resolution.
  • Separate smooth animation logic from discrete frame logic.
  • Use iFrame for startup and state transitions, not motion speed.
  • Use iChannelTime when 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.

Visit Shadertoy

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.