Eye-candy demos for your terminal — each at two resolutions.
Small C demos built on termpaint. Each one renders as classic ASCII cells everywhere, and at pixel resolution on terminals that speak the kitty graphics protocol (kitty, iTerm2, WezTerm, ...).
No dependencies beyond a C compiler and make — termpaint is vendored as a
submodule and built into the binaries.
There are also a few keyboard-driven menu-driven apps — a to-do list, a file browser, 2048, a system monitor, and a process-exec tracer — built on the same toolkit, with the panels floating over the same animated backdrops.
git clone --recurse-submodules https://github.com/binRick/termfun.git
cd termfun
make
./build/fireworks-gfx # pixel mode on kitty-protocol terminals, cells elsewhere
./build/matrix-gfx
./build/ripples-gfx
./build/fire-gfx
./build/starfield-gfx
./build/fireworks # pure cell versions
./build/matrix
./build/ripples
./build/fire
./build/starfield
./build/taskman-gfx # menu-driven apps (kitty backdrop + cells)
./build/filer-gfx
./build/2048-gfx
./build/sysmon-gfx
sudo ./build/extracer-gfx # process-exec tracer (Linux + root; wraps extrace)If you already cloned without --recurse-submodules, make initializes the
submodule for you. The make run-<demo> targets (run, run-gfx,
run-matrix-gfx, run-ripples-gfx, ...) build and launch in one step; the
./*.sh scripts do the same.
All recordings below are real iTerm2 sessions running the demos.
Rockets, bursts, a twinkling sky, and a city skyline.
Renders into an RGBA framebuffer transmitted to the terminal every frame:
- Additive glow — sparks are radial gradients that sum into the framebuffer, so overlapping bursts get hot in the middle.
- Decay trails — every channel decays toward zero each frame instead of being cleared, so rockets and sparks leave natural fading streaks.
- Transparent sky — alpha follows the glow, so the status bar and your terminal background show through where nothing is burning.
- Tear-free — frames are double-buffered image ids wrapped in
synchronized-output (
DECSET 2026), so cells and pixels land atomically.
Pure termpaint: particles pick a glyph by intensity (✸ ● • ·), positions are
tracked at half-cell vertical resolution, and the skyline windows flicker.
This is also exactly what fireworks-gfx shows on terminals without graphics
support.
Digital rain with half-width katakana, bright stream heads, and fading tails.
Press c to cycle colour schemes (green, cyan, amber, ...).
The rain falls on its own pixel glyph grid, denser than the cell grid, using randomly generated 5×7 bitmap glyphs with a phosphor glow around each stream head.
Text-glyph rain on the cell grid. On kitty-protocol terminals this version
adds a soft pixel bloom layer around the stream heads; set MATRIX_CELLS=1
for the pure text experience shown here.
A rain-dappled pool: a damped wave equation lit by the slope of the surface, with sun-coloured glints on the steepest crests. Click anywhere to toss in a stone.
The wave field runs at framebuffer resolution and every pixel is shaded by its slope, as if lit from the upper left; steep crests spill over into a specular sun colour. The pool's alpha fades out under the status bar.
The same waves on a half-cell-resolution height field: each cell averages
two sim rows and picks from a calm-to-sparkle glyph ramp (· ~ ≈ ✦),
brightening with the lit side of each swell.
The classic demoscene fire: a heat field that cools in chunky random
quanta as it rises, carving the flames into ragged tongues. Click to lob
a fireball, space to flare the burner, a to snuff it and watch the
flames die out.
The Doom fire algorithm on 2×2 pixel blocks: every cell scatters its heat to a randomly jittered spot one row up — the collisions and holes are what keep the tongues coherent. Alpha follows the heat, so the flames burn straight over your terminal background.
The same heat field at half-cell resolution, rendered as an
ember-to-blaze glyph ramp (· : ~ * # @) through a four-stop palette —
c swaps fire for blue gas, toxic green, or violet plasma.
Stars streaming past the camera toward a vanishing point that cruises around on autopilot — or wherever you click. Space punches a hyperjump.
Every star draws an additive glow segment from where it was last frame to where it is now, into a framebuffer that decays instead of clearing — so motion leaves warp streaks that stretch with speed and bend when you steer.
The same flight projected onto the cell grid at half-cell vertical
resolution, each star picking a glyph from a depth ramp (· • * ✦) and
brightening as it approaches.
Beyond the eye-candy, termfun ships a few interactive TUI apps — real
keyboard-driven programs built on the same termpaint foundation, sharing a
small widget toolkit (tui.c). Each renders as plain cells everywhere and, on
kitty-protocol terminals, floats its panels over an animated pixel backdrop
— the UI stays crisp text; only the wallpaper behind it is pixels. A per-cell
cut-out mask punches the wallpaper transparent wherever a panel is drawn, so
the panels read as floating windows.
All recordings below are real iTerm2 sessions, captured with
tools/record_iterm.py — same as the demos above.
Add, edit, complete, reorder and delete tasks; the list persists to
~/.termfun-tasks, and a progress bar tracks how much is done.
Keys: ↑↓/j/k move · Space toggle done · a add · e edit · d delete
· J/K reorder · c clear completed · g/G top/bottom · q quit.
The to-do panel floats over a slow indigo-to-teal aurora; the cut-out mask keeps the panel and bars opaque while the wallpaper drifts in the margins.
The same UI over a quiet dark gradient — rounded panel, full-row selection
highlight, ○/✓ checkboxes, an inline add/edit field and a live progress bar.
A scrolling directory list on the left, a live preview/details pane on the right (text peek, size, mtime, item counts). Directories sort first; files show right-aligned human sizes.
Keys: ↑↓ move · Enter/→ open dir · ←/Backspace up · . toggle
hidden · r refresh · g/G top/bottom · q quit.
Both panes float over a slate-to-cyan aurora, with the wallpaper showing in the gutter between them and around the edges.
The two-pane browser on a quiet gradient: colour-coded entries (directories, symlinks, executables), a scroll indicator, and a sanitized text preview.
Slide the board; equal tiles merge; reach 2048. Live score with your best kept
in ~/.termfun-2048, one-step undo, and win/lose overlays.
Keys: ↑↓←→ / WASD / HJKL slide · u undo · n/r new game · q quit.
The classic colour tiles float on a warm animated plasma that glows through the gaps between tiles and around the board.
The same colour-graded tiles (tui_fill background blocks with centred
numbers) on a quiet gradient, with centred overlays for win/game-over.
A live dashboard: CPU / memory / disk gauges, load average, uptime, core count
and OS, plus the top processes by CPU — refreshing on a timer (+/- to
tune). Stats come from mach/sysctl on macOS.
Keys: r refresh now · +/- interval · q quit.
The gauge and process panels float over a dark, scrolling neon grid.
The same dashboard over a quiet gradient: threshold-coloured gauge bars (green → amber → red) and a ranked process table.
Pick one or more processes, then watch a live tree of every program their
subtrees exec(). Each selected process becomes a labelled root (▼) and the
commands it spawns nest beneath it, colour-tagged per root so several traces
read clearly at once. The picker is a ps --forest tree — just start typing
to search (subtree-aware: typing sshd shows sshd and every process running
under it) — or launch straight into a search with extracer sshd (or
EXTRACER_FILTER=sshd). It's a front-end for
extrace: the process picker works
anywhere ps does, but the trace pane is Linux-only and needs root
(CAP_NET_ADMIN, for the kernel proc connector) — it runs one extrace -p PID
per selection and streams their output.
Keys — picker: type to search · ↑↓ move (hold to accelerate) · mouse
wheel scroll · click or Space to select · Enter trace · Esc clear
search / quit. Trace: ↑↓/wheel scroll · PgUp/PgDn page · f follow · b
back to the picker · q quit.
The process picker and the live exec tree float over a warm magenta plasma; the cut-out mask keeps the panels opaque while the wallpaper drifts in the margins.
The same two-pane workflow over a quiet gradient: a filterable process list with
·/✓ selection marks, then the colour-tagged exec tree — each traced parent
(▼) with its exec()'d children nested under ├ connectors.
| Key | fireworks | matrix | ripples | fire | starfield |
|---|---|---|---|---|---|
space |
launch a rocket | spawn a wave of drops | drop a stone | flare the burner | hyperjump |
| mouse click | rocket at the pointer | drop at the pointer | stone at the pointer | fireball at the pointer | steer to the pointer |
a |
toggle the auto show | — | toggle the rain | snuff/relight the burner | toggle the autopilot |
c |
— | cycle the colour scheme | cycle the colour scheme | cycle the colour scheme | cycle the colour scheme |
+ / - |
auto launch rate | fall speed | rain rate | flame height | warp speed |
q / Esc |
quit | quit | quit | quit | quit |
Each demo and app reads env vars with its own prefix (FIREWORKS_*,
MATRIX_*, RIPPLES_*, FIRE_*, STARFIELD_*, and TASKMAN_*, FILER_*,
G2048_*, SYSMON_*):
| Env var | Default | Effect |
|---|---|---|
*_FPS |
demo-specific | target frame rate (gfx) |
*_MAXDIM |
512 (640 for apps) |
framebuffer size cap; 1024 for sharper, larger frames |
*_CELLS |
unset | set to force cell rendering even on kitty terminals |
Frames are uncompressed base64 RGBA, so bandwidth scales with
MAXDIM² × FPS — if a remote connection feels sluggish, turn one of them
down.
The apps also take a few app-specific vars:
| Env var | Default | Effect |
|---|---|---|
TASKMAN_FILE |
~/.termfun-tasks |
where tasks are stored |
FILER_DIR |
current dir | directory to open on launch |
G2048_FILE |
~/.termfun-2048 |
where the best score is stored |
SYSMON_INTERVAL |
1.5 |
seconds between stat refreshes |
EXTRACER_BIN |
extrace (on PATH) |
path to the extrace binary to run |
EXTRACER_FILTER |
unset | initial picker search (also extracer <term>) |
Graphics support is detected before termpaint takes over the tty: the
demo asks the terminal to validate (not display) a 1×1 image and chases it
with a DA1 query. Every terminal answers DA1; only kitty-protocol terminals
answer the graphics query first (kitty_gfx.c).
Each frame is then transmitted as a chunked, base64-encoded RGBA image
(a=T,f=32) stretched over the full cell grid, layered above the text with
alpha. Old frames are deleted by id after the replacement is on screen.
kitty_probe.c is a standalone tool that runs the same detection by hand and
hex-dumps the terminal's raw replies — handy for checking what your terminal
and multiplexer actually pass through:
./build/kitty_probe| File | What |
|---|---|
fireworks.c, fireworks-gfx.c |
fireworks demo (cells / kitty pixels) |
matrix.c, matrix-gfx.c |
digital rain demo (cells / kitty pixels) |
ripples.c, ripples-gfx.c |
water ripples demo (cells / kitty pixels) |
fire.c, fire-gfx.c |
demoscene fire demo (cells / kitty pixels) |
starfield.c, starfield-gfx.c |
warp starfield demo (cells / kitty pixels) |
taskman.c, taskman-gfx.c |
to-do list app (cells / kitty backdrop) |
filer.c, filer-gfx.c |
file browser app (cells / kitty backdrop) |
2048.c, 2048-gfx.c |
sliding-tile game (cells / kitty backdrop) |
sysmon.c, sysmon-gfx.c |
system dashboard app (cells / kitty backdrop) |
extracer.c, extracer-gfx.c |
process-exec tracer, a front-end for extrace (cells / kitty backdrop) |
tui.{c,h} |
shared TUI toolkit (panels, lists, input, backdrop) |
kitty_gfx.{c,h} |
minimal kitty graphics protocol support library |
kitty_probe.c |
terminal graphics-support probe |
tools/ |
recording & screenshot harness for the README media |
termpaint/ |
termpaint submodule |
All recordings are real terminal sessions: captured from iTerm2 with
tools/record_iterm.py, which launches each demo in a window, drives it,
and assembles the frames into the GIFs above.
Demo code is 0BSD. termpaint is Boost Software License 1.0.



















