ascii-city
v0.3.1
Published
An INFINITE seeded ASCII cyberpunk city you can walk around and drive in, right in your terminal (or browser). GTA before GTA 6, in glyphs.
Maintainers
Readme
ascii-city
A walkable — and drivable — ASCII cyberpunk city. By default it's an ambient
stroll: traffic cruises the avenues, pedestrians wander the sidewalks, and you
can hop into any car (E) just to drive around. Set engine.game: true in
js/config.js for GTA mode: missions, money, wanted stars, and cops. Three
layers:
- The base —
js/citygen.js, a seeded city generator: spec in, deterministic city model out. Pure data, no rendering, strictly printable ASCII (0x20–0x7E). The city is INFINITE: roads sit on a jittered lattice and every block is a pure function of (seed, coordinates), sogenerateRegion()can materialize any window of it;js/world.jsslides a 160×160 window around the player (regen ~3ms, bit-identical overlap, so recentring is invisible). - The sim —
js/sim.js, the shared gameplay core: player (on foot or driving), civilian traffic, pedestrians, police + wanted stars, delivery missions + money, radio stations, and BFS GPS routing. - The renderers —
js/walker.js(browser) andjs/tui.js(terminal), the same first-person raycaster drawing the same sim as glyphs.
js/config.js is the single input: city goes straight into generateCity(),
engine tunes the renderer. City specs land there.
Run — browser
python3 serve.py 8670http://localhost:8670 — click to walk. WASD move, mouse look, shift run,
F or left-click shoots, E enters/exits cars, V toggles third person,
M minimap, N new seed, 1-4 glyph size. Police respond to crimes by default
(engine.police: false for a peaceful city; engine.missions: true adds
deliveries and money).
Run — terminal
npx . # from the repo, or: npm i -g . && ascii-citySame city, same raycaster, rendered as ANSI truecolor cells. V toggles a
third-person chase cam, F fires (hold in kitty terminals), E jacks cars,
and the HUD shows a speedometer while driving. In terminals
with the kitty keyboard protocol (iTerm2, Ghostty, kitty, WezTerm) you get
real hold-to-move: hold WASD, shift to run, hold arrows or drag the mouse
to look. Elsewhere (e.g. Terminal.app, which never reports key releases)
it falls back to latched movement: w walks and keeps going, s stops
then reverses, space stops, a/d/arrows turn in eased steps, r
toggles run. n new city, q quits, either way. Flags: --seed,
--palette neon|terminal|amber|rust|ice, --size WxD, --fov, --dist,
and --frame to print a single frame and exit. Needs Node 18+ and a
truecolor terminal. js/tui.js mirrors js/walker.js — change the
projection or ramps in one, mirror it in the other.
The generator
import { generateCity } from './js/citygen.js';
const model = generateCity({ seed: 'ghost-harbor-412', size: { width: 200, depth: 160 } });Any subset of DEFAULT_CONFIG (see js/citygen.js) can be passed: road
spacing/widths/jitter, per-district weight/centerBias/heights/parcels/window
style/wall glyph (custom district types are kept), building tiers and antennas,
plazas, trees, every glyph. Each stage pulls from its own named RNG stream and
per-cell effects are coordinate hashes, so changing one parameter never
reshuffles unrelated parts of the city.
Model output: cells (road/sidewalk/building/park/plaza/tree), roadAxis
(lane markings), heightMap (per-cell height — what the raycaster marches),
bIndex (cell → building), buildings (footprint, tiers, district, window
style), plus blocks/trees/fountains/stats.
The walker
Per screen column: DDA front-to-back through the grid. Each cell contributes a
wall face and a top-surface strip, clipped against yCur (lowest unpainted row)
so occlusion is free. Window lattices are per-building coordinate hashes —
stable across frames. Distance fog is quantized so the glyph atlas stays small.
Debug page
http://localhost:8670/debug.html — slider playground over the generator (plan view + skyline probe). Diagnostic tool only, not the product.
Roadmap
- traffic + pedestrians (roads/sidewalks already in the model)
- interiors, lifts, elevation
- signs, rain, audio
