A six-scene demoscene production that pushes the interpreter to its edges on purpose: an LCG where there is no RND, redrawn strokes where the palette cannot be written, the sprite bank as the scrolltext, a supernova and black hole out of SOLID/COLLISION/RSPPOS, wireframe 3D from two rotations and a perspective divide, and a single-queue beeper-style soundtrack with a noise drum break. The same file runs in the standalone frontend and in the clockless screenshot host, which gets each scene as a still. Three interpreter lessons learned the hard way are recorded in the example's README and TODO.md section 4: a scalar first assigned in a GOSUB dies with its scope, predeclaring scratch runs the 128-slot variable pool dry, and a TI# busy-wait starves the PLAY queue of its one release per frame. Co-Authored-By: Claude Code (Fable 5) <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
AKLABS MEGADEMO '85
A demoscene production for akbasic, written as if it were 1985 and this dialect were the machine under the tree. Code and FX by Tachikoma, dedicated to GameCube, megagreetz to starfort.
./build-akgl/basic examples/megademo/megademo.bas
Q or ESC quits. Six scenes: a title card, a Lissajous braid, a spinning wireframe cube and octahedron — which drift together, ignite a pulsing origin point strobing the next scene's colours, swallow the camera as the perspective scale outgrows the window, and split that origin into the first two raster bars — then the raster bars proper, the sprite bank going supernova — the letters fall into the middle, a fireball throws them at the walls, and a black hole drags everything back into the centre as the transition out — and a finale that composes all of it. A sawtooth chiptune in A minor throughout, with a noise drum break, if a SID answers.
What it leans on
This is a tour of the interpreter's edges, on purpose:
- No
RND— it carries Chapter 17's LCG, seeded fromTI#. - No palette writes — the "colour cycle" on the logo is an honest redraw of the same strokes in the next colour, and the raster bars are erased and redrawn two scanlines at a time. The palette is sixteen fixed colours and the demo says so on screen.
- Eight sprites — so the lettering is the sprite bank. Each letter of GAMECUBE
is drawn with the stroke font from
examples/breakout, captured withSSHAPE, installed withSPRSAV, and tinted withSPRITE. SOLIDwalls andCOLLISION 2— the supernova registers four rectangles and reverses a bearing perRCOLLISION(n, 7). The interpreter does the physics; BASIC does one subtraction. The letters leave the blast onMOVSPR's continuous form, eight bearings fanned evenly around the compass.- The black hole is
RSPPOSplus one rotation — read each letter's position back, rotate the offset from centre, shrink it by 0.86, put it there. Thirty-four frames later everything is in the middle. The screen goes with it: an iris of collapsing blackCIRCLEs, which works because the drawing verbs clip silently at the window edge — a fireball and an iris can both start bigger than the screen. - The camera is a multiplier — the wireframe zoom-through is nothing but the perspective scale growing fourteen percent a frame until the solids' edges leave the window, which the silent clipping permits. The pulsing origin point strobes through the bar ramp table because it is the next scene: it splits, rides the Y axis to the band edges, and stretches into the first two raster bars in place.
- 3D with no matrices — the wireframe scene rotates fourteen pooled vertices
through two composed axis rotations and a perspective divide, all of it
SIN,COSand one division, with the float kept on the left of every product because the left operand decides the arithmetic. Erase is the raster bars' trick: redraw the previous frame's twenty-four edges in black. PLAYis one queue — every note holds it for its length and the whole batch plays on one instrument, so threeVtracks come out end to end, not layered. The soundtrack is therefore beeper music, the way single-channel tracker tunes were written: sixteenths that take turns being the kick (anO1root), the pumping bass (O2/O3octaves) and the rolling arpeggio (O4and up), two alternating eight-beat batches — Am F C G, then Am F Dm E with the G sharp — and every third batch a bar of noise drums. Noise is real:ENVELOPE's sixth argument is a waveform, 0 to 3, which Chapter 7 forgot to mention. Voice 1 carries the tune so the collision blips (voice 2) and scene sweeps (voice 3) never steal its channel.
It also probes for its hardware like a proper boot loader, with one TRAP per
device: no graphics refuses by name and exits, no audio mutes the soundtrack, no
text grid drops the status row, and a host that never sets the clock — which is
exactly what tools/screenshot.c is — renders each scene as a still and leaves the
finale as the picture. The same file runs everywhere; nothing hangs.
The ones that bit
A scalar first assigned inside a GOSUB dies with that call's scope. The first
cut computed text centring in a subroutine and read the result after RETURN; every
line of text rendered flush left, because the variable read back as a fresh zero.
Reads climb the scope chain, so a nested subroutine can use your globals — but a
write to a name that exists nowhere creates it in the current scope, and the scope
is not coming back. That is why Chapter 18's listings predeclare.
Then the predeclaring bit back. The obvious lesson — predeclare everything — ran
the 128-slot variable pool dry the moment the wireframes arrived: Maximum runtime variables reached. A predeclared scratch variable occupies a slot forever; a scoped
one gives its slot back at RETURN. So the rule is narrower than it first looks:
predeclare a subroutine's outputs and nothing else. This program predeclares four.
And the raster wait strangled the SID. The VSYNC label used to spin on TI#
until the jiffy turned — romantic, and fatal. The host releases at most one queued
PLAY note per frame, and a frame of 256 executed spin statements is slow enough
to drop the release rate below the tune's fifteen notes a second. The queue never
reclaims slots until it drains completely, so the backlog compounded quietly for two
scenes and then PLAY queue is full killed the show. VSYNC now sleeps a frame —
SLEEP holds at no per-step cost — and TUNE traps the overflow on hosts slower
than this one, dropping a bar instead of the whole demo. TODO.md §4 carries the
measurement.
What it wanted and could not have
Recorded properly in TODO.md §4, "What a demoscene program wanted and could not
have": RND as a real function, palette writes, GSHAPE XOR stamping, a way to ask
whether PLAY has drained, per-voice PLAY queues so three voices can actually
sound together, and a frame-edge wait that is not a busy loop. None of them blocked
the demo; all of them left a routine behind that every future game will carry too.