Library

Shared functions

The helper functions the shaders share, each with what it does, the episode that introduced it, and the code to copy. Most are a few lines long; the ones that need another function say which.

Noise and randomness

hash21

Turns a 2D point into a pseudo-random number from 0 to 1. The same point always gives the same number, and neighbouring points give unrelated ones.

Introduced in 01 · Flowing Color Field; also in 04, 05

hash21 · from episode 1
float hash21(float2 p) {
    p = fract(p * float2(123.34, 456.21));
    p += dot(p, p + 45.32);
    return fract(p.x * p.y);
}

hash22

Like hash21, but gives two random numbers for a point, such as a random position inside a grid square.

Introduced in 05 · Cracked Clay

hash22 · from episode 5
float2 hash22(float2 p) {
    float3 q = fract(float3(p.xyx) * float3(0.1031, 0.1030, 0.0973));
    q += dot(q, q.yzx + 33.33);
    return fract((q.xx + q.yz) * q.zy);
}

hashU integer hash

A whole number in, a random number from 0 to 1 out. It works on integers, so noise built on it stays exact for minutes of sound, where a float hash would lose precision.

Introduced in 05 · Cracked Clay; also in 06

hashU · from episode 5
float hashU(uint n) {
    n = (n << 13u) ^ n;
    n = n * (n * n * 15731u + 789221u) + 1376312589u;
    return float(n & 0x7fffffffu) / float(0x7fffffff);
}

vnoise value noise

Value noise: a random value at each grid point, blended smoothly between the points.

Introduced in 01 · Flowing Color Field; also in 04, 05

Calls hash21

vnoise · from episode 1
float vnoise(float2 p) {
    float2 i = floor(p), f = fract(p);
    f = f * f * (3.0 - 2.0 * f);
    float a = hash21(i), b = hash21(i + float2(1, 0));
    float c = hash21(i + float2(0, 1)), d = hash21(i + float2(1, 1));
    return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}

fbm fractal noise

Fractal noise: several octaves of value noise added together, each about twice as fine and half as strong as the one before.

Introduced in 01 · Flowing Color Field; also in 04 (changed), 05 (changed)

Calls vnoise

fbm · from episode 1
float fbm(float2 p) {
    float v = 0.0, a = 0.5;
    for (int i = 0; i < 5; i++) {
        v += a * vnoise(p);
        p = p * 2.03 + float2(17.1, 9.2);
        a *= 0.5;
    }
    return v;
}

voronoi Voronoi cells

Voronoi cells around a random point in each grid square. Returns the distance to the nearest edge between two cells, the distance to the nearest point, and a random number for the cell.

Introduced in 05 · Cracked Clay

Calls cellPoint (the episode’s own), hash21

voronoi · from episode 5
float3 voronoi(float2 x, float t) {
    float2 n = floor(x), f = fract(x);
    float2 mg = 0.0, mr = 0.0;
    float md = 8.0;
    for (int j = -1; j <= 1; j++) {
        for (int i = -1; i <= 1; i++) {
            float2 g = float2(i, j);
            float2 r = g + cellPoint(n + g, t) - f;
            float d = dot(r, r);
            if (d < md) { md = d; mr = r; mg = g; }
        }
    }
    float near = sqrt(md);
    md = 8.0;
    for (int j = -2; j <= 2; j++) {
        for (int i = -2; i <= 2; i++) {
            float2 g = mg + float2(i, j);
            float2 r = g + cellPoint(n + g, t) - f;
            if (dot(mr - r, mr - r) > 0.00001) {
                md = min(md, dot(0.5 * (mr + r), normalize(r - mr)));
            }
        }
    }
    return float3(md, near, hash21(n + mg));
}

snoise 1D noise

Smooth noise along one line, from -1 to 1: random values at whole numbers, blended between them.

Introduced in 01 · Flowing Color Field

Calls sh11 (the episode’s own)

snoise · from episode 1
float snoise(float x) {
    float i = floor(x), f = fract(x);
    f = f * f * (3.0 - 2.0 * f);
    return mix(sh11(i), sh11(i + 1.0), f) * 2.0 - 1.0;
}

noise1 sound noise

Noise for sound: random values joined rate times a second. A lower rate sounds darker.

Introduced in 05 · Cracked Clay

Calls hashU

noise1 · from episode 5
float noise1(float t, float rate, uint seed) {
    float x = t * rate;
    float i = floor(x), f = fract(x);
    f = f * f * (3.0 - 2.0 * f);
    uint k = uint(i) + seed * 7919u;
    return mix(hashU(k), hashU(k + 1u), f) * 2.0 - 1.0;
}

Shapes in 2D

sdCircle circle distance

The distance to a circle of radius r: negative inside, zero on the edge, positive outside.

Introduced in 02 · Morphing Shapes; also in 04

sdCircle · from episode 2
float sdCircle(float2 p, float r) {
    return length(p) - r;
}

sdBox box distance

The distance to a rectangle centred on the origin, with b its half-width and half-height. It is exact inside and outside, corners included.

Introduced in 02 · Morphing Shapes; also in 04

sdBox · from episode 2
float sdBox(float2 p, float2 b) {
    float2 d = abs(p) - b;
    return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}

sdTriangle triangle distance

The distance to an equilateral triangle centred on the origin, r setting its size.

Introduced in 02 · Morphing Shapes

sdTriangle · from episode 2
float sdTriangle(float2 p, float r) {
    const float k = 1.7320508;
    p.x = abs(p.x) - r;
    p.y = p.y + r / k;
    if (p.x + k * p.y > 0.0) p = float2(p.x - k * p.y, -k * p.x - p.y) / 2.0;
    p.x -= clamp(p.x, -2.0 * r, 0.0);
    return -length(p) * sign(p.y);
}

Shapes in 3D

sdSphere sphere distance

The distance to a sphere of radius r: the circle's formula, in three dimensions.

Introduced in 03 · Floating 3D Scene; also in 06

sdSphere · from episode 3
float sdSphere(float3 p, float r) {
    return length(p) - r;
}

sdBox 3D box distance

The distance to a box centred on the origin, with b its half-size along each axis.

Introduced in 03 · Floating 3D Scene

sdBox · from episode 3
float sdBox(float3 p, float3 b) {
    float3 d = abs(p) - b;
    return length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0);
}

sdTorus torus distance

The distance to a ring lying flat: R is the radius of the ring, r the thickness of its tube.

Introduced in 03 · Floating 3D Scene

sdTorus · from episode 3
float sdTorus(float3 p, float R, float r) {
    float2 q = float2(length(p.xz) - R, p.y);
    return length(q) - r;
}

Combining shapes

smin smooth minimum

The smooth minimum. Like min, but when two distances are within k of each other it pulls the result below both, so two shapes melt into one with a smooth neck between them.

Introduced in 02 · Morphing Shapes; also in 03, 04, 06

smin · from episode 2
float smin(float a, float b, float k) {
    float h = max(k - abs(a - b), 0.0) / k;
    return min(a, b) - h * h * k * 0.25;
}

Moving space

rot rotation

Turns a pair of coordinates around the origin by the angle a, in radians.

Introduced in 03 · Floating 3D Scene; also in 04

rot · from episode 3
float2 rot(float2 p, float a) {
    float c = cos(a), s = sin(a);
    return float2(c * p.x - s * p.y, s * p.x + c * p.y);
}

kaleido kaleidoscope fold

Folds the plane into n slices around the centre, each mirrored down its middle, so whatever is drawn in one half-slice appears in all of them. spin turns the folds and twist turns them more the further out they are.

Introduced in 04 · Kaleidoscope

kaleido · from episode 4
float2 kaleido(float2 q, float n, float spin, float twist) {
    float r = length(q);
    float a = atan2(q.y, q.x) + spin + twist * r;
    float slice = TAU / n;
    // Angular repetition: every slice lands on the first.
    a -= slice * floor(a / slice);
    // Mirror fold: the two halves of a slice reflect each other.
    a = abs(a - slice * 0.5);
    return r * float2(cos(a), sin(a));
}

Raymarching and light

normalAt surface normals

The direction a surface faces at a point: how the scene's distance changes for a small step along each axis. Its arguments are whatever the episode's map takes.

Introduced in 03 · Floating 3D Scene; also in 06 (changed)

Calls map (the episode’s own)

normalAt · from episode 3
float3 normalAt(float3 p, float t, float lift, float k) {
    const float2 e = float2(0.001, 0.0);
    return normalize(float3(
        map(p + e.xyy, t, lift, k) - map(p - e.xyy, t, lift, k),
        map(p + e.yxy, t, lift, k) - map(p - e.yxy, t, lift, k),
        map(p + e.yyx, t, lift, k) - map(p - e.yyx, t, lift, k)));
}

softShadow soft shadows

Marches from a surface point towards the light. The nearer the ray passes to something, compared with how far it has gone, the darker the shadow, so its edge fades over a band; w sets how wide.

Introduced in 06 · Molten Metaballs

Calls map (the episode’s own)

softShadow · from episode 6
float softShadow(float3 ro, float3 rd, float w,
                 float t, int n, float rise, float k) {
    float res = 1.0;
    float s = 0.02;
    for (int i = 0; i < 48; i++) {
        float h = map(ro + rd * s, t, n, rise, k);
        res = min(res, h / (w * s));
        if (res < 0.001 || s > 6.0) break;
        s += clamp(h, 0.01, 0.3);
    }
    res = clamp(res, 0.0, 1.0);
    return res * res * (3.0 - 2.0 * res);
}

ambientOcclusion ambient occlusion

Takes a few short steps out along the normal. Where the scene is nearer than the step, something blocks light from that side, so creases and corners come out darker.

Introduced in 06 · Molten Metaballs

Calls map (the episode’s own)

ambientOcclusion · from episode 6
float ambientOcclusion(float3 pos, float3 nrm,
                       float t, int n, float rise, float k) {
    float occ = 0.0;
    float weight = 1.0;
    for (int i = 0; i < 5; i++) {
        float h = 0.02 + 0.12 * float(i);
        occ += (h - map(pos + nrm * h, t, n, rise, k)) * weight;
        weight *= 0.8;
    }
    return clamp(1.0 - 2.0 * occ, 0.0, 1.0);
}

Colour

kiln the kiln palette

The Ray Kiln palette: a number from 0 to 1 becomes near-black, then ember, orange, gold and white-hot.

Introduced in 01 · Flowing Color Field; also in 02, 03, 04, 05, 06

kiln · from episode 1
float3 kiln(float t) {
    float3 c0 = float3(0.020, 0.012, 0.020);
    float3 c1 = float3(0.280, 0.040, 0.030);
    float3 c2 = float3(0.880, 0.260, 0.050);
    float3 c3 = float3(1.000, 0.680, 0.200);
    float3 c4 = float3(1.000, 0.970, 0.840);
    t = clamp(t, 0.0, 1.0);
    float3 c = mix(c0, c1, smoothstep(0.00, 0.22, t));
    c = mix(c, c2, smoothstep(0.22, 0.50, t));
    c = mix(c, c3, smoothstep(0.50, 0.76, t));
    return mix(c, c4, smoothstep(0.76, 1.00, t));
}

Sound

voice a voice with overtones

A tone with its next three overtones. ph is the phase in cycles; bright goes from 0, a pure sine, to 1, a fuller and reedier sound.

Introduced in 03 · Floating 3D Scene; also in 06

voice · from episode 3
float voice(float ph, float bright) {
    const float tau = 6.2831853;
    return sin(tau * ph) + bright * (0.45 * sin(2.0 * tau * ph) + 0.25 * sin(3.0 * tau * ph) + 0.12 * sin(4.0 * tau * ph));
}

morphWave a morphing wave

One wave blending a sine, a square and a triangle, by the three weights in w. The square and triangle are built from their first four odd harmonics, so they stay soft.

Introduced in 02 · Morphing Shapes

morphWave · from episode 2
float morphWave(float ph, float3 w) {
    const float tau = 6.2831853;
    float sq = 0.0, tri = 0.0;
    for (int n = 1; n <= 7; n += 2) {
        float s = sin(tau * float(n) * ph);
        sq += s / float(n);
        tri += s * ((n / 2) % 2 == 0 ? 1.0 : -1.0) / float(n * n);
    }
    return w.x * sin(tau * ph) + w.y * sq * 0.75 + w.z * tri * 0.81;
}

pluck a plucked note

A plucked note: a sine and its octave that start fast and die away. since is the time since the note began.

Introduced in 04 · Kaleidoscope

pluck · from episode 4
float pluck(float f, float since, float t) {
    float env = (1.0 - exp(-since * 250.0)) * exp(-since * 5.0);
    return (sin(TAU * f * t) + 0.3 * sin(TAU * 2.0 * f * t)) * env;
}

The words used here, such as distance field or octave, are explained in the glossary.