A screensaver and desktop toy that flies through the Gibson: the fictional supercomputer from the 1995 film Hackers, visualized as a city of translucent data towers standing in blackness on a circuit-board floor.
This is a Rust + wgpu rewrite of John Serafino's 2015 Irrlicht/OpenGL
original. One renderer core drives every platform — a macOS screen saver, a
Windows .scr, a Linux xscreensaver hack, a windowed desktop app, and a
WebGPU/WebGL2 web build — and the visuals are graded against real frames
from the film rather than guessed; see
docs/film-reference.md for the sources and the
production research.
https://paulkiernan.github.io/hack-the-gibson/
The page uses WebGPU where the browser exposes it and falls back to WebGL2 otherwise — so Chrome 113+, Safari 26+, and Firefox with WebGPU enabled run the WebGPU path, and any WebGL2-capable browser without WebGPU still gets the scene through the fallback. The URL accepts query parameters — see the Web entry under Settings.
To host it yourself instead, hack-the-gibson-web.zip on the
Releases page
is a static build of the same page. Unzip it and serve it over HTTP — the
wasm module needs server headers, so a file:// URL will not work:
unzip hack-the-gibson-web.zip
cd web
python3 -m http.server 8080Prebuilt downloads for every host are attached to the
Releases page.
A release is published whenever a semver tag is pushed — the version alone,
with no v prefix — and the tag must equal the workspace version in
Cargo.toml character for character (the release workflow refuses to build
if it does not). A prerelease tag such as 2.1.0-rc.1 publishes as a GitHub
prerelease rather than as the latest stable release. If you want the tip of
main instead, every host builds from source — see Building.
Each release carries the same six assets:
| Asset | What it is |
|---|---|
Gibson.saver.zip |
macOS screen saver bundle, universal (arm64 + x86_64) |
Gibson.scr |
Windows screen saver |
hack-the-gibson-macos-universal.tar.gz |
macOS windowed desktop app |
hack-the-gibson-linux-x86_64.tar.gz |
Linux desktop app / xscreensaver hack, including gibson.xml |
hack-the-gibson-web.zip |
the static web build, for self-hosting |
SHA256SUMS |
checksums covering every asset above |
Download the asset you want and SHA256SUMS into the same directory, then
verify the download before installing:
# macOS
shasum -a 256 -c SHA256SUMS
# Linux
sha256sum -c SHA256SUMSThe names in SHA256SUMS are the bare asset names above, so both commands
work from your download directory with no paths to adjust. SHA256SUMS
covers every asset in the release, so if you fetched only some of them, add
--ignore-missing (shasum -a 256 -c --ignore-missing SHA256SUMS) and the
assets you did not download are skipped instead of reported as FAILED open or read.
Requires macOS 14 or later. Either build it yourself (see
Building), or download Gibson.saver.zip (universal — Apple
silicon and Intel) and SHA256SUMS from the
Releases page.
# From your download directory, with the zip and SHA256SUMS both present:
shasum -a 256 -c --ignore-missing SHA256SUMS
# Unzip it and put the bundle where macOS looks for screen savers:
unzip Gibson.saver.zip
cp -R Gibson.saver "$HOME/Library/Screen Savers/"
# macOS quarantines browser downloads and then refuses to load the ad-hoc
# signed bundle, so clear the flag on the installed copy and its contents:
xattr -dr com.apple.quarantine "$HOME/Library/Screen Savers/Gibson.saver"
# Make the running screen-saver process pick up the new bundle:
killall legacyScreenSaver 2>/dev/null || trueThen pick The Gibson in System Settings > Wallpaper > Screen Saver.
make install-saver does the same copy-and-restart steps for a build from
source, where no quarantine step is needed because nothing was downloaded.
Gatekeeper note: the bundle is ad-hoc signed but not notarized, so the
xattr -dr line above is what makes it load — without it macOS leaves the
bundle quarantined and the screen saver simply never draws, because a
.saver is loaded inside the screen-saver process rather than launched as
an app (there is no "open anyway" dialog and no Privacy & Security prompt
for it). Stripping the attribute clears the quarantine only; the ad-hoc
signature still verifies afterwards. Verify the download against
SHA256SUMS before installing it.
Remove it with make uninstall-saver, or by deleting the bundle from
~/Library/Screen Savers/.
Download Gibson.scr from the
Releases page
and install it the classic way: right-click the file and choose Install,
or copy it to C:\Windows\System32\Gibson.scr and pick "The Gibson" in
Settings > Personalization > Lock screen > Screen saver settings. Windows
marks downloaded programs as internet-sourced, so if SmartScreen warns,
choose More info > Run anyway, or clear the mark first with
Unblock-File .\Gibson.scr.
The same binary understands /s (full screen), /p <hwnd> (preview tile),
and /c (opens the settings file). Full steps are in
platform/windows/README.md. To build it
yourself instead:
cargo build --release -p gibson-app
copy target\release\gibson-app.exe Gibson.scr
Verified on real hardware. The Windows host has been run and confirmed working by the maintainer, in addition to being built and tested in CI on every push.
gibson-app doubles as an xscreensaver "external window" hack. The
hack-the-gibson-linux-x86_64.tar.gz asset on the
Releases page
contains the binary and the gibson.xml descriptor, and unpacks into a
hack-the-gibson-linux-x86_64/ directory:
# From your download directory, with the tarball and SHA256SUMS present:
sha256sum -c --ignore-missing SHA256SUMS
tar -xzf hack-the-gibson-linux-x86_64.tar.gz
cd hack-the-gibson-linux-x86_64
# Put the binary on PATH as `gibson`, and the descriptor where
# xscreensaver looks for it:
install -Dm755 gibson-app "$HOME/.local/bin/gibson"
sudo install -Dm644 gibson.xml /usr/share/xscreensaver/config/gibson.xmlThen add this line to ~/.xscreensaver (create it with xscreensaver-demo
first if needed):
programs: gibson -root
That line resolves gibson on PATH; give the absolute path instead if
~/.local/bin is not on yours.
cargo build --release -p gibson-app produces the same binary from source,
and platform/linux/gibson.xml is the
descriptor the tarball ships. Full steps and the settings-dialog note are in
platform/linux/README.md.
Unverified on real hardware: the xscreensaver host is compile-verified in
CI only — nobody has run it yet. It is also X11 only: on a Wayland session
use swayidle plus gibson-app --fullscreen instead (see the same file for
the exact command).
Grab hack-the-gibson-macos-universal.tar.gz (universal) or
hack-the-gibson-linux-x86_64.tar.gz (glibc x86_64) from the
Releases page;
each unpacks into a directory holding gibson-app:
tar -xzf hack-the-gibson-macos-universal.tar.gz
# macOS quarantines the tarball on download and the binary is not
# notarized, so clear the flag before running it:
xattr -d com.apple.quarantine hack-the-gibson-macos-universal/gibson-app
./hack-the-gibson-macos-universal/gibson-appOr run it from source:
cargo run --release -p gibson-appA windowed flythrough opens; press Esc or Q to quit. Useful flags (see
--help for the full list):
--fullscreen borderless fullscreen
--snapshot out.png --size 1920x1080 render one offscreen still and exit
--time 12 (seconds of simulated flight for the still;
deterministic for a fixed --seed)
--speed 0.8 fly speed
--bank 0.9 banking strength
--palette normal|siege|cycle color treatment
--grid 40 city size (grid x grid towers)
--pulses 200 lane pulse streaks
--seed 12345 reproducible city and flight (0 = time-derived)
--render-scale 0.5 internal resolution multiplier
--no-bloom --no-motion-blur --no-crt disable individual effects
--config /path/to/gibson.toml alternate settings file
Platform status, stated plainly: the macOS saver, the Windows .scr, the
desktop app, and the web build have all been run on real hardware. The Linux
xscreensaver host is compile-verified in CI only — nobody has run it yet;
it is documented as untested in platform/linux/README.md and should be
treated accordingly.
One settings struct drives every host. All values are clamped to legal ranges on load, so a bad config file, CLI value, or query parameter can never put the renderer out of bounds.
| Setting | Default | Range | Effect |
|---|---|---|---|
fly_speed |
0.55 | 0.05–3 | Speed along the flight path (segments per second) |
bank_strength |
0.45 | −3–3 | How hard the camera banks into turns |
bank_max_degrees |
32 | 0–60 | Maximum bank angle, degrees |
bank_smoothing |
0.55 | 0.05–2 | Bank low-pass time constant, seconds |
palette |
normal |
normal, siege, cycle |
Color treatment (blues / attack oranges / timed cycling) |
palette_cycle_seconds |
240 | 10–3600 | Seconds between switches in cycle mode |
bloom |
0.35 | 0–2 | Bloom intensity; 0 disables bloom |
motion_blur |
0.5 | 0–1 | Motion-blur strength; 0 disables |
grain |
0.03 | 0–0.2 | Film-grain amount; 0 disables |
crt |
0.35 | 0–1 | CRT-overlay strength (scanlines, aperture grille, curvature, phosphor bloom, edge vignette); 0 disables |
render_scale |
1.0 | 0.25–1 | Internal resolution multiplier; lower is cheaper on slow GPUs |
grid |
60 | 8–120 | City size: grid x grid towers |
pulses |
700 | 0–2000 | Number of pulse streaks down the lanes |
seed |
0 | any 64-bit integer | City, atlas, and floor seed; 0 derives one from the clock |
preview |
false | true / false | Screensaver-preview mode; hosts set this themselves |
High-DPI rendering is capped automatically. On screen (desktop window, macOS
saver, xscreensaver hack, Windows .scr, web canvas) the render target is capped
at 2.8 megapixels, whatever the display resolution: a 2940x1912 saver drawable
renders at about 2078x1352 and the compositor upscales. Rendering cost is
fill-rate proportional (~5 ms per megapixel), so the cap is what keeps a 60 Hz
frame achievable, and above it the extra pixels are detail nobody sees in motion.
render_scale is unchanged and still multiplies on top of the cap, so the
slider/setting works in both directions (a 0.5 there halves the capped target
again). Offscreen renders are never capped: --snapshot --size WxH always
produces exactly WxH, which is what the committed screenshots and CI rely on.
Where each host stores or accepts them:
-
macOS Options sheet (System Settings > Wallpaper > Screen Saver > Options…, or right-click the preview): sliders Fly speed (0.2–1.2), Banking (0–1), and CRT overlay (0–1); a Palette popup with Normal / Siege / Cycle; and Bloom glow, Motion blur, and Film grain checkboxes (unchecked disables the effect). Changes apply on the next activation.
-
gibson.toml: created automatically — with every key, its default, and a comment — the first time a desktop host or--snapshotrun starts, at<config-dir>/hack-the-gibson/gibson.toml, where<config-dir>is~/Library/Application Supporton macOS,~/.configon Linux, and%APPDATA%on Windows. The Windows/cmode also ensures the file exists and opens it in your editor. Unknown keys are ignored; missing keys fall back to defaults. -
Desktop CLI:
--speed,--bank,--palette,--grid,--pulses,--grain,--crt,--seed,--render-scale, and the--no-bloom/--no-motion-blur/--no-crtswitches, applied on top of the config file. -
Web: the same names as query parameters on the demo URL, except
fly_speedisspeed,bank_strengthisbank, andrender_scaleisscale:https://paulkiernan.github.io/hack-the-gibson/?palette=cycle&grid=40&pulses=200Supported:
speed,bank,palette,grid,pulses,seed,scale,bloom,motionblur,grain,crt.
Precedence everywhere is: built-in defaults < gibson.toml < command-line
or query overrides.
Prerequisites:
- Rust stable (the workspace pins
stableinrust-toolchain.toml) - For the web build:
rustup target add wasm32-unknown-unknownandcargo install wasm-pack --locked - For a universal macOS saver (arm64 + x86_64 slices):
rustup target add aarch64-apple-darwin x86_64-apple-darwin(the saver Makefile defaults to both; build just your arch withmake -C platform/macos ARCHS=arm64)
Root Makefile targets (each delegates to cargo or a platform Makefile):
| Target | What it does |
|---|---|
make app |
Run the windowed desktop app |
make snapshot |
Render docs/screenshots/lane.png (1920x1080, t=12 s) |
make web |
wasm-pack build of crates/gibson-web into web/pkg |
make saver |
Build platform/macos/build/Gibson.saver |
make install-saver / make uninstall-saver |
Install / remove the saver |
make test |
cargo test --workspace --exclude gibson-web |
make clean |
cargo clean |
Notable: the macOS saver builds with Command Line Tools only — no Xcode
required. It is a Makefile that compiles the Rust core to a staticlib,
links it into a Swift dylib with swiftc, lipos the architectures
together, wraps the result in a .saver bundle, and ad-hoc codesigns it
(recipe proven by the PerfectoWeb/Gibson saver). CI tests and builds every
target — macOS (tests + saver), Windows (tests + .scr), Linux (tests +
xscreensaver host), and wasm (web bundle) — and pushing a semver tag (no v
prefix) publishes all of those builds as a
release
with checksums.
The workspace is a set of small crates with one contract crate,
gibson-types, that everything compiles against:
- Deterministic procedural content. A 64-bit
seedreproduces the same city, text, and floor exactly. The text atlas (gibson-atlas) generates 64 layers of 256x768 RGBA: 32 tower-face panels, each with two text variants that share byte-identical block geometry. Panels 0–28 are dense mosaics of mono-text blocks — hex dumps, numeric columns, keyword rows, bar-chart glyphs, framed and inverse-video blocks — in IBM Plex Mono; panels 28–32 are hero directory lists (Michroma, the Eurostile-Extended- style face) whose entries double as individually highlightable blocks. The floor (gibson-floor) generates a toroidal 96x96-cell circuit board on which the towers are the integrated circuits: each tower footprint is an IC package with its own pin ring, and nets run tower to tower between those pins, routed octilinearly so a 45-degree jog costs less than a right angle, alongside bus bundles, power rails, ground pours, vias and silkscreen. - The city. A
grid x gridarray of towers standing in the lanes of the 2015 world grid. Towers are instanced translucent glass boxes 12 units wide and 44–110 tall, drawn double-sided so back-face text bleeds through the body. In the shader, each text block clears and redraws top-down on its own cycle, alternating between the two text variants; occasionally a block on a nearby tower lights up in the palette's highlight color, and pulse streaks run down the lanes between towers. - The camera. The closed-loop flight path was rescued from the 2015 C++ waypoints (z-negated for a right-handed Y-up world), and the banking algorithm — a low-passed yaw rate driving a smoothed roll — was ported from the SceneKit fork that this project grew out of.
- The frame graph. The scene renders at
render_scaleinto an HDR (16-bit float) target: floor, towers, pulses, then a bloom prefilter with a downsample/upsample chain, motion blur by depth reprojection against the previous frame, and a final composite applying ACES tonemapping, chromatic aberration, film grain, and vignette — plus, whencrt > 0, a CRT treatment (scanlines, aperture grille, screen curvature, phosphor smear) as the last step. Distant towers fade through a blue haze to black, and the whole look is graded against the film reference in docs/film-reference.md.
This is a spare-time project. If it made you smile and you feel like saying thanks, you can buy me a coffee:
- John Serafino — the 2015 Irrlicht original this is a rewrite of.
- dherberger — the 2026 macOS SceneKit/Metal
.saverfork that proved the screensaver path and whose banking code and grid conventions carried over; the fork was merged back upstream to become this project. - The film crew whose work is being reproduced: Peter Chiang (VFX supervisor), Tim Field (VFX producer), and Neville Brody (type design). The film's towers were built as clear perspex prisms with printed text cels, shot on motion control at Pinewood, and designed after Muriel Cooper's MIT "Information Landscapes".
GPL-3.0-or-later (see GPL.txt). The bundled fonts — Michroma Regular and IBM Plex Mono Medium — are SIL Open Font License; their license text ships in assets/fonts/OFL-Michroma.txt and assets/fonts/OFL-IBMPlexMono.txt.



