1 Day 2 Code
Complete, standalone code for each shader referenced in Day 2. Each listing can be copied directly into Shadertoy and run immediately.
1.1 Common Functions
These helper functions are used throughout Day 2:
// Normalize screen coordinates to centered, aspect-corrected space
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 2.5;
}
// Complex multiplication: (a + bi)(c + di)
vec2 cmul(vec2 z, vec2 w) {
return vec2(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x);
}
// Cosine palette for smooth coloring
vec3 palette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.00, 0.33, 0.67);
return a + b * cos(6.28318 * (c * t + d));
}1.2 mandelbrot-zoom
Animated zoom into the Mandelbrot set with smooth coloring.
vec2 cmul(vec2 z, vec2 w) {
return vec2(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x);
}
vec3 palette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.00, 0.33, 0.67);
return a + b * cos(6.28318 * (c * t + d));
}
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
float zoom = pow(1.5, mod(iTime, 30.0));
return uv * 2.5 / zoom;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
// Zoom into the seahorse valley
vec2 center = vec2(-0.745, 0.186);
vec2 c = center + normalize_coord(fragCoord);
vec3 color = vec3(0.0, 0.0, 0.0);
vec2 z = vec2(0.0, 0.0);
int i;
for (i = 0; i < 200; i++) {
if (length(z) > 2.0) {
// Smooth coloring
float log_zn = log(length(z) * length(z)) / 2.0;
float nu = log(log_zn / log(2.0)) / log(2.0);
float smooth_iter = float(i) + 1.0 - nu;
float t = smooth_iter / 200.0;
color = palette(t * 4.0);
break;
}
z = cmul(z, z) + c;
}
fragColor = vec4(color, 1.0);
}1.3 mandelbrot-bw
Black and white Mandelbrot set.
vec2 cmul(vec2 z, vec2 w) {
return vec2(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x);
}
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 2.5;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 c = normalize_coord(fragCoord);
c.x = c.x - 0.5; // shift left to center the interesting part
vec3 color = vec3(0.0, 0.0, 0.0);
vec2 z = vec2(0.0, 0.0);
for (int i = 0; i < 100; i++) {
if (length(z) > 2.0) {
color = vec3(1.0, 1.0, 1.0);
break;
}
z = cmul(z, z) + c;
}
fragColor = vec4(color, 1.0);
}1.4 mandelbrot-gray
Mandelbrot set with grayscale iteration coloring.
vec2 cmul(vec2 z, vec2 w) {
return vec2(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x);
}
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 2.5;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 c = normalize_coord(fragCoord);
c.x = c.x - 0.5;
vec3 color = vec3(0.0, 0.0, 0.0);
vec2 z = vec2(0.0, 0.0);
int i;
for (i = 0; i < 100; i++) {
if (length(z) > 2.0) break;
z = cmul(z, z) + c;
}
if (i < 100) {
float t = float(i) / 100.0;
float gray = 1.0 - t;
color = vec3(gray, gray, gray);
}
fragColor = vec4(color, 1.0);
}1.5 mandelbrot-color
Mandelbrot set with cosine palette coloring.
vec2 cmul(vec2 z, vec2 w) {
return vec2(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x);
}
vec3 palette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.00, 0.33, 0.67);
return a + b * cos(6.28318 * (c * t + d));
}
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 2.5;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 c = normalize_coord(fragCoord);
c.x = c.x - 0.5;
vec3 color = vec3(0.0, 0.0, 0.0);
vec2 z = vec2(0.0, 0.0);
int i;
for (i = 0; i < 100; i++) {
if (length(z) > 2.0) break;
z = cmul(z, z) + c;
}
if (i < 100) {
float t = float(i) / 100.0;
color = palette(t);
}
fragColor = vec4(color, 1.0);
}1.6 julia-static
Julia set with fixed parameter.
vec2 cmul(vec2 z, vec2 w) {
return vec2(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x);
}
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 2.5;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
// Fixed parameter
vec2 c = vec2(-0.7, 0.27015);
// z starts at pixel position
vec2 z = normalize_coord(fragCoord);
vec3 color = vec3(0.0, 0.0, 0.0);
int i;
for (i = 0; i < 100; i++) {
if (length(z) > 2.0) break;
z = cmul(z, z) + c;
}
if (i < 100) {
float t = float(i) / 100.0;
float gray = 1.0 - t;
color = vec3(gray, gray, gray);
}
fragColor = vec4(color, 1.0);
}1.7 julia-explorer
Interactive Julia set explorer: Mandelbrot as parameter space, Julia set overlaid.
vec2 cmul(vec2 z, vec2 w) {
return vec2(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x);
}
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 2.5;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
// Get c from mouse position
vec2 c = normalize_coord(iMouse.xy);
c.x = c.x - 0.5;
// Default to interesting value if no mouse
if (iMouse.x < 1.0) {
c = vec2(-0.7, 0.27015);
}
// Mandelbrot iteration (for background)
vec2 mc = p;
mc.x = mc.x - 0.5;
vec2 mz = vec2(0.0, 0.0);
int m_i;
for (m_i = 0; m_i < 100; m_i++) {
if (length(mz) > 2.0) break;
mz = cmul(mz, mz) + mc;
}
// Julia iteration (for foreground)
vec2 jz = p;
int j_i;
for (j_i = 0; j_i < 100; j_i++) {
if (length(jz) > 2.0) break;
jz = cmul(jz, jz) + c;
}
// Mandelbrot grayscale (faded to serve as background)
vec3 color = vec3(1.0, 1.0, 1.0);
if (m_i < 100) {
float t = float(m_i) / 100.0;
float gray = 0.6 + 0.4 * (1.0 - t); // range [0.6, 1.0]
color = vec3(gray, gray, gray);
} else {
color = vec3(0.5, 0.5, 0.5); // Mandelbrot interior
}
// Julia grayscale (overlaid)
if (j_i < 100) {
float t = float(j_i) / 100.0;
float gray = 1.0 - t;
color = vec3(gray, gray, gray);
} else {
color = vec3(0.0, 0.0, 0.0); // Julia interior
}
// Draw red dot at c position
vec2 c_pos = c;
c_pos.x = c_pos.x + 0.5;
if (length(p - c_pos) < 0.05) {
color = vec3(1.0, 0.0, 0.0);
}
fragColor = vec4(color, 1.0);
}1.8 inversion-toggle
Circle inversion visualization with toggling.
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 4.0;
}
vec2 invert(vec2 p) {
return p / dot(p, p);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
vec2 p_inv = invert(p);
// Toggle between original and inverted every second
float time = fract(iTime * 0.5);
vec2 q;
if (time < 0.5) {
q = p;
} else {
q = p_inv;
}
vec3 color = vec3(0.1, 0.1, 0.15);
// Draw the unit circle
float d_unit = abs(length(p) - 1.0);
if (d_unit < 0.02) color = vec3(0.5, 0.5, 0.5);
// Draw a vertical line at x = 2
if (abs(q.x - 2.0) < 0.02) color = vec3(1.0, 1.0, 0.0);
// Draw a horizontal line at y = 1.5
if (abs(q.y - 1.5) < 0.02) color = vec3(1.0, 1.0, 0.0);
// Draw a circle centered at (2, 0) with radius 0.5
float d_circle = abs(length(q - vec2(2.0, 0.0)) - 0.5);
if (d_circle < 0.02) color = vec3(1.0, 1.0, 0.0);
fragColor = vec4(color, 1.0);
}1.9 inversion-grid
Circle inversion of a grid.
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 4.0;
}
vec2 invert(vec2 p) {
return p / dot(p, p);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
vec2 p_inv = invert(p);
// Toggle
float time = fract(iTime * 0.5);
vec2 q;
if (time < 0.5) {
q = p;
} else {
q = p_inv;
}
vec3 color = vec3(0.1, 0.1, 0.15);
// Draw the unit circle
float d_unit = abs(length(p) - 1.0);
if (d_unit < 0.02) color = vec3(0.5, 0.5, 0.5);
// Draw a grid using mod
vec2 grid = mod(q, 0.5);
if (grid.x < 0.02 || grid.y < 0.02) color = vec3(1.0, 1.0, 0.0);
fragColor = vec4(color, 1.0);
}1.10 inversion-moving
Inversion through a moving circle.
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 4.0;
}
struct Circle {
vec2 center;
float radius;
};
vec2 invert(vec2 p, Circle c) {
vec2 d = p - c.center;
return c.center + c.radius * c.radius * d / dot(d, d);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
// Animate the inversion circle
Circle inv_circle;
inv_circle.center = vec2(sin(iTime) * 0.5, cos(iTime * 0.7) * 0.5);
inv_circle.radius = 1.0 + 0.3 * sin(iTime * 1.3);
vec2 p_inv = invert(p, inv_circle);
vec3 color = vec3(0.1, 0.1, 0.15);
// Draw the inversion circle
float d_inv = abs(length(p - inv_circle.center) - inv_circle.radius);
if (d_inv < 0.02) color = vec3(0.5, 0.5, 0.5);
// Draw a grid in the inverted space
vec2 grid = mod(p_inv, 0.5);
if (grid.x < 0.02 || grid.y < 0.02) color = vec3(1.0, 1.0, 0.0);
fragColor = vec4(color, 1.0);
}1.11 apollonian-setup
The four mutually tangent circles.
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 6.0;
}
struct Circle {
vec2 center;
float radius;
};
float distToCircle(vec2 p, Circle c) {
return abs(length(p - c.center) - c.radius);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
float r = 1.0;
float triSide = 2.0 * r;
float circumradius = triSide / sqrt(3.0);
Circle c1 = Circle(vec2(0.0, circumradius), r);
Circle c2 = Circle(vec2(-circumradius * sqrt(3.0)/2.0, -circumradius * 0.5), r);
Circle c3 = Circle(vec2(circumradius * sqrt(3.0)/2.0, -circumradius * 0.5), r);
Circle outer = Circle(vec2(0.0, 0.0), circumradius + r);
vec3 color = vec3(0.1, 0.1, 0.15);
if (distToCircle(p, c1) < 0.03) color = vec3(1.0, 0.3, 0.3);
if (distToCircle(p, c2) < 0.03) color = vec3(0.3, 1.0, 0.3);
if (distToCircle(p, c3) < 0.03) color = vec3(0.3, 0.3, 1.0);
if (distToCircle(p, outer) < 0.03) color = vec3(1.0, 1.0, 1.0);
fragColor = vec4(color, 1.0);
}1.12 apollonian-iterated
Full Apollonian gasket with iteration.
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 6.0;
}
struct Circle {
vec2 center;
float radius;
};
vec2 invert(vec2 p, Circle c) {
vec2 d = p - c.center;
return c.center + c.radius * c.radius * d / dot(d, d);
}
float distToCircle(vec2 p, Circle c) {
return abs(length(p - c.center) - c.radius);
}
bool isInside(vec2 p, Circle c) {
return length(p - c.center) < c.radius;
}
vec3 palette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.00, 0.33, 0.67);
return a + b * cos(6.28318 * (c * t + d));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
float r = 1.0;
float triSide = 2.0 * r;
float circumradius = triSide / sqrt(3.0);
Circle c1 = Circle(vec2(0.0, circumradius), r);
Circle c2 = Circle(vec2(-circumradius * sqrt(3.0)/2.0, -circumradius * 0.5), r);
Circle c3 = Circle(vec2(circumradius * sqrt(3.0)/2.0, -circumradius * 0.5), r);
Circle outer = Circle(vec2(0.0, 0.0), circumradius + r);
int i;
for (i = 0; i < 50; i++) {
if (isInside(p, c1)) {
p = invert(p, c1);
} else if (isInside(p, c2)) {
p = invert(p, c2);
} else if (isInside(p, c3)) {
p = invert(p, c3);
} else if (!isInside(p, outer)) {
p = invert(p, outer);
} else {
break;
}
}
float t = float(i) / 50.0;
vec3 color = palette(t);
float dMin = min(min(distToCircle(p, c1), distToCircle(p, c2)),
min(distToCircle(p, c3), distToCircle(p, outer)));
if (dMin < 0.02) color = vec3(1.0, 1.0, 1.0);
fragColor = vec4(color, 1.0);
}1.13 apollonian-final
Apollonian gasket with coloring emphasizing the limit set.
vec2 normalize_coord(vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
uv = uv - vec2(0.5, 0.5);
uv.x *= iResolution.x / iResolution.y;
return uv * 6.0;
}
struct Circle {
vec2 center;
float radius;
};
vec2 invert(vec2 p, Circle c) {
vec2 d = p - c.center;
return c.center + c.radius * c.radius * d / dot(d, d);
}
bool isInside(vec2 p, Circle c) {
return length(p - c.center) < c.radius;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 p = normalize_coord(fragCoord);
float r = 1.0;
float triSide = 2.0 * r;
float circumradius = triSide / sqrt(3.0);
Circle c1 = Circle(vec2(0.0, circumradius), r);
Circle c2 = Circle(vec2(-circumradius * sqrt(3.0)/2.0, -circumradius * 0.5), r);
Circle c3 = Circle(vec2(circumradius * sqrt(3.0)/2.0, -circumradius * 0.5), r);
Circle outer = Circle(vec2(0.0, 0.0), circumradius + r);
int i;
for (i = 0; i < 100; i++) {
if (isInside(p, c1)) {
p = invert(p, c1);
} else if (isInside(p, c2)) {
p = invert(p, c2);
} else if (isInside(p, c3)) {
p = invert(p, c3);
} else if (!isInside(p, outer)) {
p = invert(p, outer);
} else {
break;
}
}
float t = float(i) / 100.0;
float t2 = pow(t, 2.0);
vec3 color = 30.0 * vec3(t2, t2, t2);
fragColor = vec4(color, 1.0);
}