1 Day 4 Code
Complete, standalone code for each shader in Day 4. Copy any of these directly into Shadertoy.
1.1 Texture Setup
The texture examples require loading an image into iChannel0:
- Click
iChannel0at the bottom of the editor - Select the “Textures” tab
- Choose an image (try “Abstract 1” or any photograph)
1.2 Buffer Setup
The buffer examples (painting, Game of Life, waves) require a self-referencing buffer:
- Click the + tab next to “Image” and select “Buffer A”
- In Buffer A, click
iChannel0at the bottom and select “Buffer A” from the Misc tab - In Image, click
iChannel0and select “Buffer A”
1.3 texture-basic
Display a texture using UV coordinates.
Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
fragColor = texture(iChannel0, uv);
}1.4 texture-flip
Flip an image horizontally.
Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv.x = 1.0 - uv.x;
fragColor = texture(iChannel0, uv);
}1.5 texture-wavy
Animated wavy distortion.
Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
// Offset x based on y position and time
uv.x += 0.03 * sin(uv.y * 20.0 + iTime * 2.0);
fragColor = texture(iChannel0, uv);
}1.6 texture-swirl
Swirl distortion centered on the image.
Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
vec2 center = vec2(0.5);
vec2 offset = uv - center;
float dist = length(offset);
float angle = atan(offset.y, offset.x);
// Rotate more near the center
float swirl = 2.0 * exp(-dist * 3.0);
angle += swirl;
uv = center + dist * vec2(cos(angle), sin(angle));
fragColor = texture(iChannel0, uv);
}1.7 texture-magnify
Magnifying glass that follows the mouse. Click and drag to move.
Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
vec2 mouse = iMouse.xy / iResolution.xy;
float dist = length(uv - mouse);
float radius = 0.15;
if (dist < radius) {
// Inside lens: sample closer to mouse position (zoom in)
float zoom = 2.0;
uv = mouse + (uv - mouse) / zoom;
}
fragColor = texture(iChannel0, uv);
}1.8 paint-broken
The broken painting program — no persistence. Demonstrates why we need buffers.
Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
float d = length(fragCoord - iMouse.xy);
if (iMouse.z > 0.0 && d < 10.0) {
fragColor = vec4(1.0); // White brush
} else {
fragColor = vec4(0.0, 0.0, 0.0, 1.0); // Black background
}
}1.9 paint-basic
Persistent painting. Click and drag to draw.
Buffer A:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec4 prev = texelFetch(iChannel0, ivec2(fragCoord), 0);
float d = length(fragCoord - iMouse.xy);
if (iMouse.z > 0.0 && d < 10.0) {
fragColor = vec4(1.0);
} else {
fragColor = prev;
}
}Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
fragColor = texelFetch(iChannel0, ivec2(fragCoord), 0);
}1.10 paint-fade
Painting with fading trails.
Buffer A:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec4 prev = texelFetch(iChannel0, ivec2(fragCoord), 0);
prev *= 0.99;
float d = length(fragCoord - iMouse.xy);
if (iMouse.z > 0.0 && d < 10.0) {
fragColor = vec4(1.0);
} else {
fragColor = prev;
}
}Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
fragColor = texelFetch(iChannel0, ivec2(fragCoord), 0);
}1.11 game-of-life
Conway’s Game of Life.
Buffer A:
float hash(vec2 p) {
p = fract(p * vec2(234.34, 435.345));
p += dot(p, p + 34.23);
return fract(p.x * p.y);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (iFrame == 0) {
float random = hash(fragCoord);
fragColor = vec4(step(0.5, random));
return;
}
ivec2 p = ivec2(fragCoord);
float self = texelFetch(iChannel0, p, 0).r;
float neighbors =
texelFetch(iChannel0, p + ivec2(-1, -1), 0).r +
texelFetch(iChannel0, p + ivec2( 0, -1), 0).r +
texelFetch(iChannel0, p + ivec2( 1, -1), 0).r +
texelFetch(iChannel0, p + ivec2(-1, 0), 0).r +
texelFetch(iChannel0, p + ivec2( 1, 0), 0).r +
texelFetch(iChannel0, p + ivec2(-1, 1), 0).r +
texelFetch(iChannel0, p + ivec2( 0, 1), 0).r +
texelFetch(iChannel0, p + ivec2( 1, 1), 0).r;
float alive = 0.0;
if (self == 1.0) {
if (neighbors == 2.0 || neighbors == 3.0) {
alive = 1.0;
}
} else {
if (neighbors == 3.0) {
alive = 1.0;
}
}
fragColor = vec4(vec3(alive), 1.0);
}Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
fragColor = texelFetch(iChannel0, ivec2(fragCoord), 0);
}1.12 wave-equation
Basic wave equation with Gaussian initial condition.
Buffer A:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (iFrame == 0) {
vec2 center = iResolution.xy * 0.5;
float d = length(fragCoord - center);
float u = 3.0 * exp(-d * d / 100.0);
fragColor = vec4(u, 0.0, 0.0, 1.0);
return;
}
ivec2 p = ivec2(fragCoord);
float u = texelFetch(iChannel0, p, 0).r;
float v = texelFetch(iChannel0, p, 0).g;
float u_n = texelFetch(iChannel0, p + ivec2( 0, 1), 0).r;
float u_s = texelFetch(iChannel0, p + ivec2( 0, -1), 0).r;
float u_e = texelFetch(iChannel0, p + ivec2( 1, 0), 0).r;
float u_w = texelFetch(iChannel0, p + ivec2(-1, 0), 0).r;
float laplacian = u_n + u_s + u_e + u_w - 4.0 * u;
// Symplectic Euler: update velocity first, then position
float dt = 0.3;
float c = 1.0;
float newV = v + dt * c * c * laplacian;
float newU = u + dt * newV;
fragColor = vec4(newU, newV, 0.0, 1.0);
}Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
float u = texelFetch(iChannel0, ivec2(fragCoord), 0).r;
u *= 3.0;
vec3 color;
if (u > 0.0) {
color = mix(vec3(0.0), vec3(1.0, 0.5, 0.0), u);
} else {
color = mix(vec3(0.0), vec3(0.0, 0.3, 1.0), -u);
}
fragColor = vec4(color, 1.0);
}1.13 wave-interactive
Wave equation with mouse interaction. Click to add ripples.
Buffer A:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (iFrame == 0) {
fragColor = vec4(0.0);
return;
}
ivec2 p = ivec2(fragCoord);
float u = texelFetch(iChannel0, p, 0).r;
float v = texelFetch(iChannel0, p, 0).g;
float u_n = texelFetch(iChannel0, p + ivec2( 0, 1), 0).r;
float u_s = texelFetch(iChannel0, p + ivec2( 0, -1), 0).r;
float u_e = texelFetch(iChannel0, p + ivec2( 1, 0), 0).r;
float u_w = texelFetch(iChannel0, p + ivec2(-1, 0), 0).r;
float laplacian = u_n + u_s + u_e + u_w - 4.0 * u;
// Symplectic Euler: update velocity first, then position
float dt = 0.3;
float c = 1.0;
float newV = v + dt * c * c * laplacian;
if (iMouse.z > 0.0) {
float d = length(fragCoord - iMouse.xy);
float sigma = 10.0;
newV += 0.01 * exp(-d * d / (2.0 * sigma * sigma));
}
float newU = u + dt * newV;
fragColor = vec4(newU, newV, 0.0, 1.0);
}Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
float u = texelFetch(iChannel0, ivec2(fragCoord), 0).r;
u *= 3.0;
vec3 color;
if (u > 0.0) {
color = mix(vec3(0.0), vec3(1.0, 0.5, 0.0), u);
} else {
color = mix(vec3(0.0), vec3(0.0, 0.3, 1.0), -u);
}
fragColor = vec4(color, 1.0);
}1.14 wave-circle
Wave equation in a circular domain. Click to add ripples.
Buffer A:
bool inDomain(vec2 fragCoord, vec2 resolution) {
vec2 center = resolution * 0.5;
float radius = min(resolution.x, resolution.y) * 0.4;
return length(fragCoord - center) < radius;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (iFrame == 0) {
fragColor = vec4(0.0);
return;
}
if (!inDomain(fragCoord, iResolution.xy)) {
fragColor = vec4(0.0);
return;
}
ivec2 p = ivec2(fragCoord);
float u = texelFetch(iChannel0, p, 0).r;
float v = texelFetch(iChannel0, p, 0).g;
float u_n = texelFetch(iChannel0, p + ivec2( 0, 1), 0).r;
float u_s = texelFetch(iChannel0, p + ivec2( 0, -1), 0).r;
float u_e = texelFetch(iChannel0, p + ivec2( 1, 0), 0).r;
float u_w = texelFetch(iChannel0, p + ivec2(-1, 0), 0).r;
float laplacian = u_n + u_s + u_e + u_w - 4.0 * u;
// Symplectic Euler
float dt = 0.3;
float c = 1.0;
float newV = v + dt * c * c * laplacian;
if (iMouse.z > 0.0) {
float d = length(fragCoord - iMouse.xy);
float sigma = 10.0;
newV += 0.01 * exp(-d * d / (2.0 * sigma * sigma));
}
float newU = u + dt * newV;
fragColor = vec4(newU, newV, 0.0, 1.0);
}Image:
bool inDomain(vec2 fragCoord, vec2 resolution) {
vec2 center = resolution * 0.5;
float radius = min(resolution.x, resolution.y) * 0.4;
return length(fragCoord - center) < radius;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (!inDomain(fragCoord, iResolution.xy)) {
fragColor = vec4(0.1, 0.1, 0.1, 1.0);
return;
}
float u = texelFetch(iChannel0, ivec2(fragCoord), 0).r;
u *= 3.0;
vec3 color;
if (u > 0.0) {
color = mix(vec3(0.0), vec3(1.0, 0.5, 0.0), u);
} else {
color = mix(vec3(0.0), vec3(0.0, 0.3, 1.0), -u);
}
fragColor = vec4(color, 1.0);
}1.15 mandelbrot-waves
Wave equation inside the Mandelbrot set. Click to add ripples.
Common:
bool inDomain(vec2 fragCoord, vec2 resolution) {
vec2 center = resolution * 0.5;
float scale = min(resolution.x, resolution.y) * 0.3;
vec2 c = (fragCoord - center) / scale;
c.x -= 0.5;
vec2 z = vec2(0.0);
for (int i = 0; i < 100; i++) {
z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c;
if (dot(z, z) > 4.0) return false;
}
return true;
}Buffer A:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (iFrame == 0) {
fragColor = vec4(0.0);
return;
}
if (!inDomain(fragCoord, iResolution.xy)) {
fragColor = vec4(0.0);
return;
}
ivec2 p = ivec2(fragCoord);
float u = texelFetch(iChannel0, p, 0).r;
float v = texelFetch(iChannel0, p, 0).g;
float u_n = texelFetch(iChannel0, p + ivec2( 0, 1), 0).r;
float u_s = texelFetch(iChannel0, p + ivec2( 0, -1), 0).r;
float u_e = texelFetch(iChannel0, p + ivec2( 1, 0), 0).r;
float u_w = texelFetch(iChannel0, p + ivec2(-1, 0), 0).r;
float laplacian = u_n + u_s + u_e + u_w - 4.0 * u;
// Symplectic Euler
float dt = 0.3;
float c = 1.0;
float newV = v + dt * c * c * laplacian;
if (iMouse.z > 0.0) {
float d = length(fragCoord - iMouse.xy);
float sigma = 10.0;
newV += 0.01 * exp(-d * d / (2.0 * sigma * sigma));
}
float newU = u + dt * newV;
fragColor = vec4(newU, newV, 0.0, 1.0);
}Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (!inDomain(fragCoord, iResolution.xy)) {
fragColor = vec4(0.1, 0.1, 0.1, 1.0);
return;
}
float u = texelFetch(iChannel0, ivec2(fragCoord), 0).r;
u *= 3.0;
vec3 color;
if (u > 0.0) {
color = mix(vec3(0.0), vec3(1.0, 0.5, 0.0), u);
} else {
color = mix(vec3(0.0), vec3(0.0, 0.3, 1.0), -u);
}
fragColor = vec4(color, 1.0);
}1.16 mandelbrot-waves-hook
Hook demo: waves already bouncing inside the Mandelbrot set when the page loads.
Common:
bool inDomain(vec2 fragCoord, vec2 resolution) {
vec2 center = resolution * 0.5;
float scale = min(resolution.x, resolution.y) * 0.3;
vec2 c = (fragCoord - center) / scale;
c.x -= 0.5;
vec2 z = vec2(0.0);
for (int i = 0; i < 100; i++) {
z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c;
if (dot(z, z) > 4.0) return false;
}
return true;
}Buffer A:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (iFrame == 0) {
if (!inDomain(fragCoord, iResolution.xy)) {
fragColor = vec4(0.0);
return;
}
// Off-center Gaussian bump
vec2 center = iResolution.xy * 0.5 + vec2(50.0, 30.0);
float d = length(fragCoord - center);
float u = 3.0 * exp(-d * d / 100.0);
fragColor = vec4(u, 0.0, 0.0, 1.0);
return;
}
if (!inDomain(fragCoord, iResolution.xy)) {
fragColor = vec4(0.0);
return;
}
ivec2 p = ivec2(fragCoord);
float u = texelFetch(iChannel0, p, 0).r;
float v = texelFetch(iChannel0, p, 0).g;
float u_n = texelFetch(iChannel0, p + ivec2( 0, 1), 0).r;
float u_s = texelFetch(iChannel0, p + ivec2( 0, -1), 0).r;
float u_e = texelFetch(iChannel0, p + ivec2( 1, 0), 0).r;
float u_w = texelFetch(iChannel0, p + ivec2(-1, 0), 0).r;
float laplacian = u_n + u_s + u_e + u_w - 4.0 * u;
// Symplectic Euler
float dt = 0.3;
float c = 1.0;
float newV = v + dt * c * c * laplacian;
if (iMouse.z > 0.0) {
float d = length(fragCoord - iMouse.xy);
float sigma = 10.0;
newV += 0.01 * exp(-d * d / (2.0 * sigma * sigma));
}
float newU = u + dt * newV;
fragColor = vec4(newU, newV, 0.0, 1.0);
}Image:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
if (!inDomain(fragCoord, iResolution.xy)) {
fragColor = vec4(0.1, 0.1, 0.1, 1.0);
return;
}
float u = texelFetch(iChannel0, ivec2(fragCoord), 0).r;
u *= 3.0;
vec3 color;
if (u > 0.0) {
color = mix(vec3(0.0), vec3(1.0, 0.5, 0.0), u);
} else {
color = mix(vec3(0.0), vec3(0.0, 0.3, 1.0), -u);
}
fragColor = vec4(color, 1.0);
}