1 Day 1 Code
This appendix provides complete, standalone code for each shader referenced in Day 1. Each listing can be copied directly into Shadertoy and run immediately.
1.1 red
The simplest shader: every pixel is red.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}1.2 half-plane-pixels
Dividing the screen using raw pixel coordinates, before we learn the coordinate transformation.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec3 color;
if (fragCoord.x < iResolution.x / 2.0) {
color = vec3(1.0, 0.0, 0.0); // red on left
} else {
color = vec3(0.0, 0.0, 1.0); // blue on right
}
fragColor = vec4(color, 1.0);
}1.3 coordinates
Visualizing the coordinate system as color.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
fragColor = vec4(uv.x, uv.y, 0.0, 1.0);
}1.4 half-plane
Dividing the plane into two regions based on y-coordinate.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
float L = p.y;
vec3 color;
if (L < 0.0) {
color = vec3(1.0, 0.0, 0.0); // red below
} else {
color = vec3(0.0, 0.0, 1.0); // blue above
}
fragColor = vec4(color, 1.0);
}1.5 circle
Filled circle centered at the origin.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
float d = length(p);
float r = 1.0;
float f = d - r;
vec3 color;
if (f < 0.0) {
color = vec3(1.0, 1.0, 0.0); // yellow inside
} else {
color = vec3(0.1, 0.1, 0.3); // dark blue outside
}
fragColor = vec4(color, 1.0);
}1.6 circle-ring
Circle outline (ring) centered at the origin.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
float d = length(p);
float r = 1.0;
float eps = 0.1;
float f = abs(d - r) - eps;
vec3 color;
if (f < 0.0) {
color = vec3(1.0, 1.0, 0.0); // yellow ring
} else {
color = vec3(0.1, 0.1, 0.3); // dark background
}
fragColor = vec4(color, 1.0);
}1.7 circle-pulsing
Circle with radius that oscillates over time. Introduces iTime.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
float d = length(p);
float r = 1.0 + 0.5 * sin(iTime); // radius oscillates between 0.5 and 1.5
float f = d - r;
vec3 color;
if (f < 0.0) {
color = vec3(1.0, 1.0, 0.0); // yellow inside
} else {
color = vec3(0.1, 0.1, 0.3); // dark background
}
fragColor = vec4(color, 1.0);
}1.8 parabola
The parabola \(y = x^2\) rendered as an implicit curve.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
float F = p.y - p.x * p.x;
float eps = 0.1;
vec3 color;
if (abs(F) < eps) {
color = vec3(1.0, 1.0, 0.0); // yellow curve
} else {
color = vec3(0.1, 0.1, 0.3); // dark background
}
fragColor = vec4(color, 1.0);
}1.9 lemniscate-naive
Lemniscate of Bernoulli with naive thresholding (non-uniform thickness).
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
float a = 1.5;
float r2 = dot(p, p);
float F = r2 * r2 - a * a * (p.x * p.x - p.y * p.y);
float eps = 0.15;
vec3 color;
if (abs(F) < eps) {
color = vec3(1.0, 1.0, 0.0);
} else {
color = vec3(0.1, 0.1, 0.3);
}
fragColor = vec4(color, 1.0);
}1.10 lemniscate-gradient
Lemniscate with gradient correction for uniform thickness.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
float a = 1.5;
float r2 = dot(p, p);
float F = r2 * r2 - a * a * (p.x * p.x - p.y * p.y);
vec2 grad = vec2(
4.0 * p.x * r2 - 2.0 * a * a * p.x,
4.0 * p.y * r2 + 2.0 * a * a * p.y
);
float dist = abs(F) / max(length(grad), 0.01);
float eps = 0.05;
vec3 color;
if (dist < eps) {
color = vec3(1.0, 1.0, 0.0);
} else {
color = vec3(0.1, 0.1, 0.3);
}
fragColor = vec4(color, 1.0);
}1.11 lemniscate-animated
Cassini ovals animated through the lemniscate transition.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
// Cassini oval parameters
float c = 1.0; // half-distance between foci
float a = 0.8 + 0.4 * sin(iTime * 0.5); // animate through transition
// Implicit equation: (x² + y²)² - 2c²(x² - y²) = a⁴ - c⁴
float r2 = dot(p, p);
float c2 = c * c;
float a4 = a * a * a * a;
float c4 = c2 * c2;
float F = r2 * r2 - 2.0 * c2 * (p.x * p.x - p.y * p.y) - (a4 - c4);
// Gradient
vec2 grad = vec2(
4.0 * p.x * r2 - 4.0 * c2 * p.x,
4.0 * p.y * r2 + 4.0 * c2 * p.y
);
float dist = abs(F) / max(length(grad), 0.01);
float eps = 0.05;
vec3 color;
if (dist < eps) {
color = vec3(1.0, 1.0, 0.0);
} else {
color = vec3(0.1, 0.1, 0.3);
}
fragColor = vec4(color, 1.0);
}1.12 circle-mouse
Circle that follows the mouse position.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
// Normalize fragment coordinate
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
// Normalize mouse coordinate the same way
vec2 mouse = iMouse.xy / iResolution.xy;
mouse = mouse - vec2(0.5, 0.5);
mouse.x *= iResolution.x / iResolution.y;
mouse = mouse * 4.0;
// Circle centered at mouse
float d = length(p - mouse);
float r = 0.5;
vec3 color;
if (d < r) {
color = vec3(1.0, 0.9, 0.2);
} else {
color = vec3(0.1, 0.1, 0.3);
}
fragColor = vec4(color, 1.0);
}1.13 sun-earth
Sun at mouse click position with orbiting earth.
vec2 normalize_coord(vec2 coord) {
vec2 uv = coord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 4.0;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
// Sun follows mouse position
vec2 sun = normalize_coord(iMouse.xy);
// Earth orbits the sun
float orbit_radius = 0.8;
vec2 earth = sun + orbit_radius * vec2(cos(iTime), sin(iTime));
// Draw sun (larger, yellow)
float d_sun = length(p - sun);
// Draw earth (smaller, blue)
float d_earth = length(p - earth);
vec3 color = vec3(0.02, 0.02, 0.05); // dark background
if (d_sun < 0.3) {
color = vec3(1.0, 0.9, 0.2); // yellow sun
}
if (d_earth < 0.15) {
color = vec3(0.2, 0.5, 1.0); // blue earth
}
fragColor = vec4(color, 1.0);
}1.14 folium-mouse
Folium of Descartes with mouse-controlled level set.
vec2 normalize_coord(vec2 coord) {
vec2 uv = coord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 4.0;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
// Fixed parameter a
float a = 1.5;
// Map mouse x to level set value c in [-2, 2]
float c = mix(-2.0, 2.0, iMouse.x / iResolution.x);
// Folium of Descartes: x³ + y³ - 3axy = c
float F = p.x*p.x*p.x + p.y*p.y*p.y - 3.0*a*p.x*p.y - c;
// Gradient: ∇F = (3x² - 3ay, 3y² - 3ax)
vec2 grad = vec2(3.0*p.x*p.x - 3.0*a*p.y, 3.0*p.y*p.y - 3.0*a*p.x);
float dist = abs(F) / max(length(grad), 0.01);
vec3 color;
if (dist < 0.05) {
color = vec3(1.0, 1.0, 0.0);
} else {
color = vec3(0.1, 0.1, 0.3);
}
fragColor = vec4(color, 1.0);
}1.15 elliptic-curve
Single elliptic curve with mouse-controlled parameters. Colors by topology: gold (one component), blue (two components), red (singular).
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
// Mouse controls (a, b) parameters
float a = mix(-2.0, 1.0, iMouse.x / iResolution.x);
float b = mix(-2.0, 2.0, iMouse.y / iResolution.y);
// Discriminant: 4a³ + 27b²
float disc = 4.0 * a * a * a + 27.0 * b * b;
// Elliptic curve: y² = x³ + ax + b
float F = p.y * p.y - p.x * p.x * p.x - a * p.x - b;
vec2 grad = vec2(-3.0 * p.x * p.x - a, 2.0 * p.y);
float dist = abs(F) / max(length(grad), 0.01);
vec3 bg = vec3(0.05, 0.05, 0.1);
vec3 color = bg;
if (dist < 0.05) {
// Color by topology
if (abs(disc) < 0.3) {
color = vec3(1.0, 0.2, 0.2); // red for singular
} else if (disc > 0.0) {
color = vec3(1.0, 0.85, 0.3); // gold for one component
} else {
color = vec3(0.3, 0.5, 0.8); // blue for two components
}
}
fragColor = vec4(color, 1.0);
}1.16 elliptic-family
Family of elliptic curves with mouse-controlled parameters. Gold for one component, blue for two components, red for singular. Curves fade with distance from center.
vec2 normalize_coord(vec2 coord) {
vec2 uv = coord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 4.0;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
// Mouse picks (a, b) in parameter space
float a = mix(-2.0, 1.0, iMouse.x / iResolution.x);
float b_center = mix(-2.0, 2.0, iMouse.y / iResolution.y);
vec3 color = vec3(0.05, 0.05, 0.1);
vec3 oneComponent = vec3(1.0, 0.85, 0.3); // gold
vec3 twoComponent = vec3(0.3, 0.5, 0.8); // blue
vec3 singularColor = vec3(1.0, 0.2, 0.2); // red
for (int j = -15; j <= 15; j++) {
float b = b_center + float(j) * 0.15;
float dist_from_center = abs(float(j));
// Discriminant: 4a³ + 27b²
float disc = 4.0 * a * a * a + 27.0 * b * b;
float F = p.y * p.y - p.x * p.x * p.x - a * p.x - b;
vec2 grad = vec2(-3.0 * p.x * p.x - a, 2.0 * p.y);
float dist = abs(F) / max(length(grad), 0.01);
float thickness = 0.05 / (1.0 + dist_from_center * 0.8);
// Color by topology
vec3 curveColor;
if (abs(disc) < 0.3) {
curveColor = singularColor;
} else if (disc > 0.0) {
curveColor = oneComponent;
} else {
curveColor = twoComponent;
}
float brightness = 1.0 / (1.0 + dist_from_center * 0.4);
curveColor *= brightness;
if (dist < thickness) {
color = curveColor;
}
}
fragColor = vec4(color, 1.0);
}1.17 grid-circles
Grid of circles with square cells.
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
vec2 p = uv * 4.0;
float aspect = iResolution.x / iResolution.y;
float N = 5.0; // number of columns
float L = (4.0 * aspect) / N; // cell size
vec2 cell_id = floor(p / L);
vec2 cell_p = mod(p + vec2(L/2.0, L/2.0), L) - vec2(L/2.0, L/2.0);
// Checkerboard background
float checker = mod(cell_id.x + cell_id.y, 2.0);
vec3 bg = mix(vec3(0.15, 0.15, 0.25), vec3(0.25, 0.15, 0.15), checker);
// Circle in each cell
float d = length(cell_p);
float r = L * 0.35;
vec3 color;
if (d < r) {
color = vec3(1.0, 1.0, 0.0);
} else {
color = bg;
}
fragColor = vec4(color, 1.0);
}1.18 Notes
Coordinate Setup
Most shaders use this standard coordinate setup:
vec2 uv = fragCoord / iResolution.xy; // normalize to [0,1]
uv = uv - vec2(0.5, 0.5); // center origin
uv.x *= iResolution.x / iResolution.y; // aspect correction
vec2 p = uv * 4.0; // scale to [-2, 2] rangeHelper Function
For shaders using mouse input, we define:
vec2 normalize_coord(vec2 coord) {
vec2 uv = coord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 4.0;
}Gradient Correction
For implicit curves \(F(x,y) = 0\) with uniform thickness:
float dist = abs(F) / max(length(grad), 0.01);where grad is \(\nabla F\) computed analytically.