Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A cookbook

Shaders worth stealing. Each one is whole: put it in your game, give it to a layer or a sprite, and change the numbers until it’s yours.

Sepia

An old photograph: grey first, then tinted brown.

SCREEN 320, 240
SHADER "sepia"
    WHEN DRAWING
        c = IMAGE
        g = c.r * 0.3 + c.g * 0.59 + c.b * 0.11
        SET COLOR = RGBA(MIN(g * 1.2, 1.0), g, g * 0.8, c.a)
    END WHEN
END SHADER

LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("sepia")
PAUSE

Negative

Every color turned to its opposite, and the alpha left alone so a sprite keeps its shape.

SCREEN 320, 240
SHADER "negative"
    WHEN DRAWING
        c = IMAGE
        SET COLOR = RGBA(1.0 - c.r, 1.0 - c.g, 1.0 - c.b, c.a)
    END WHEN
END SHADER

LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("negative")
PAUSE

Pixelate

Big pixels: every pixel in a block reads the same spot, the block’s corner. Turn blocks down for bigger ones.

SCREEN 320, 240
SHADER "pixelate"
    USE NUMBER blocks DEFAULT 40
    WHEN DRAWING
        SET COLOR = OWNTEXTURE(FLOOR(UV * blocks) / blocks)
    END WHEN
END SHADER

LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("pixelate")
PAUSE

Old monitor

Dark lines across the screen, and the red and blue each pulled a little to one side, the way a cheap old monitor smears them.

SCREEN 320, 240
SHADER "monitor"
    USE NUMBER split DEFAULT 1.0
    USE NUMBER lines DEFAULT 0.3
    WHEN DRAWING
        red = OWNTEXTURE(UV + PX(split, 0.0))
        green = IMAGE
        blue = OWNTEXTURE(UV - PX(split, 0.0))
        row = STEP(0.5, FRACT(UV.y * SCREEN_SIZE.y / 2.0))
        shade = 1.0 - lines * row
        SET COLOR = RGBA(red.r * shade, green.g * shade, blue.b * shade, green.a)
    END WHEN
END SHADER

LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("monitor")
PAUSE

UV.y * SCREEN_SIZE.y is which row of the layer’s pixels this is, so every other pair of rows is darker, however big the window is.

Under water

Each row of the picture swings from side to side, a little later than the one above it.

SCREEN 320, 240
SHADER "underwater"
    USE NUMBER size DEFAULT 2.0
    USE NUMBER waves DEFAULT 20.0
    WHEN DRAWING
        swing = SIN(UV.y * waves + TIME * 3.0) * size
        c = OWNTEXTURE(UV + PX(swing, 0.0))
        SET COLOR = MIX(c, RGBA(0.0, 0.3, 0.6, c.a), 0.25)
    END WHEN
END SHADER

LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("underwater")
PAUSE

size is in pixels, by way of PX, so it’s the same swing on any layer.

Heat haze

Like under water, but small and fast, and only near the bottom, over a fire or a hot road.

SCREEN 320, 240
SHADER "haze"
    WHEN DRAWING
        low = SMOOTHSTEP(0.5, 1.0, UV.y)
        swing = SIN(UV.y * 90.0 + TIME * 12.0) * 1.5 * low
        SET COLOR = OWNTEXTURE(UV + PX(swing, 0.0))
    END WHEN
END SHADER

LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("haze")
PAUSE

Hit flash

A sprite that’s hit goes white and fades back, from a behavior, so your program only has to say when.

SCREEN 320, 240
SHADER "white"
    USE NUMBER amount DEFAULT 0
    WHEN DRAWING
        c = IMAGE
        SET COLOR = MIX(c, RGBA(1.0, 1.0, 1.0, c.a), amount)
    END WHEN
END SHADER

BEHAVIOR "hit"
    USE SHADER "white" !! param("amount") start(1) to(0) duration(12)
END BEHAVIOR

LOADIMG "hero.png", "hero"
ENABLE SPRITE 0
SET SPRITE 0 !! image("hero") x(140) y(100) scale((3, 3))
SHOW SPRITE 0
DO
    IF COOLDOWN(45)
        SET SPRITE 0 !! behavior("hit")
    END IF
LOOP

Burning away

A dissolve with a glowing edge: each pixel has a random number, goes when gone passes it, and glows orange just before it does.

SCREEN 320, 240
SHADER "burn"
    USE NUMBER gone DEFAULT 0
    WHEN DRAWING
        c = IMAGE
        n = RND(FLOOR(UV.x * 40.0), FLOOR(UV.y * 40.0))
        keep = STEP(gone, n)
        glow = keep * (1.0 - STEP(gone + 0.08, n))
        lit = MIX(c, RGBA(1.0, 0.5, 0.1, c.a), glow)
        SET COLOR = RGBA(lit.r, lit.g, lit.b, lit.a * keep)
    END WHEN
END SHADER

LOADIMG "hero.png", "hero"
ENABLE SPRITE 0
SET SPRITE 0 !! image("hero") x(140) y(100) scale((3, 3)) shader("burn")
SHOW SPRITE 0
gone = 0
DO
    IF COOLDOWN(1)
        gone = gone + 0.01
        IF gone > 1.1
            gone = 0
        END IF
        SET SPRITE 0 !! shader("burn") shader_params({gone: gone})
    END IF
LOOP

keep is whether the pixel’s still there, and glow is whether it’s still there but won’t be for much longer: there, and not past gone + 0.08 yet.

Glitch

Most of the time, nothing. Every so often, for a moment, rows of the picture jump sideways and the colors come apart.

SCREEN 320, 240
SHADER "glitch"
    USE NUMBER often DEFAULT 0.15
    WHEN DRAWING
        moment = FLOOR(TIME * 8.0)
        on = 1.0 - STEP(often, RND(moment))
        row = FLOOR(UV.y * 24.0)
        jump = (RND(row, moment) - 0.5) * 30.0 * on
        moved = UV + PX(jump, 0.0)
        red = OWNTEXTURE(moved + PX(4.0 * on, 0.0))
        rest = OWNTEXTURE(moved)
        SET COLOR = RGBA(red.r, rest.g, rest.b, rest.a)
    END WHEN
END SHADER

LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("glitch")
PAUSE

Eight times a second, moment changes, and RND(moment) decides whether this eighth of a second glitches: on is 1 about 15% of the time. While it’s on, each of 24 bands of rows jumps by its own random amount, and the red is read from four pixels further along than the rest.