Space Taxi In Space: Rebuilding a C64 Classic Around Friendship, Humor, and Procedural Everything
It started with a Commodore 64 in a crawlspace. Behind the water heater, wrapped in a garbage bag from 1990, next to a disk drive we named Jared. The 5.25” floppy still worked. The screen flickered blue. And then, from a speaker that had no business still functioning, a voice said:
HEY, TAXI!
Four 50-year-old men became ten-year-olds again in about half a second.
This is the story of rebuilding Space Taxi — not as a faithful port, but as a game about the four of us, told through the thing that brought us together in the first place.
The Original Space Taxi (1984)
John Kutcher’s Space Taxi for the Commodore 64 was a minor miracle. In 64 kilobytes of RAM, on a machine with a 1 MHz processor, it had speech synthesis. An actual digitized voice calling “Hey, taxi!” and “Pad 1, please!” — through a SID chip that was designed for bleeps and bloops. It felt like science fiction.
The gameplay was simple: fly a taxi between numbered landing pads, pick up passengers, deliver them to the pad they request, don’t crash. Gravity pulls you down. Thrust pushes you up. Lateral jets steer left and right. Land too hard and you explode. That was it. And we played it for hundreds of hours.
What made it stick wasn’t the mechanics — it was the voice. A computer talking to you in 1984 was magic. That voice created a relationship between the player and the game that no amount of pixel art could match. Forty years later, hearing “Hey, taxi!” still triggers the same response: I need to go pick someone up.
Making It Personal
The original Space Taxi had anonymous passengers. Ours has four: Eric, Steve, Pierre, and Tony. Real people. Childhood friends. Each one mapped to a gameplay personality:
| Character | Color | Role | Personality |
|---|---|---|---|
| Eric | Green | The Optimizer | Tracks everything, runs spreadsheets on life, says things like “Sofi’s up 8% today” while waiting for a ride |
| Steve | Orange | The Storyteller | Turns everything into narrative, calls coin flips, quotes football commentary at inappropriate moments |
| Pierre | Blue | The Anarchist | Quiet pragmatist who says things like “Understandable. Have a nice death.” |
| Tony | Yellow | The Heart | Just happy to be here, confused by options, suggests pizza as conflict resolution |
Each character has 20 idle lines they mutter while waiting on a pad. Eric gives stock tips. Steve calls coin flips and argues with invisible referees. Pierre offers nihilistic accounting observations. Tony admits he has no idea what’s happening and suggests naps.
These aren’t generic NPC barks — they’re how my friends actually talk. The specific rhythm of 40 years of shared references. When Eric says “Don’t sell yourself short, kid” while waiting on Pad 3, it’s because that’s what Eric actually says.
Procedural Voice Generation
The original C64 Space Taxi was famous for its voice synthesis. We approached the audio problem from two directions.
Pre-recorded character voices: A Python pipeline generates 44 MP3 files — hey_taxi, pad_1_please through pad_9_please, and up_please for each of the four characters. These live in sounds/eric/, sounds/steve/, etc., and get loaded at startup if present.
GDScript procedural synth fallback: If the MP3s aren’t available (or for all the SFX), audio_manager.gd builds everything from scratch at 22,050 Hz using AudioStreamWAV. Square waves, saw waves, sine waves, noise — syllable by syllable, with envelope shaping for attack and decay.
Here’s what “Pad 3, please” sounds like in code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func _build_pad_voice(pad_num: int) -> AudioStreamWAV:
var data := PackedByteArray()
# "P" consonant — noise burst
data.append_array(_generate_tone(0, 0.035, "noise", 0.40))
# "a" vowel
data.append_array(_generate_tone(290, 0.14, "saw", 0.55))
# "d" consonant — square tap + noise
data.append_array(_generate_tone(180, 0.03, "square", 0.40))
data.append_array(_generate_tone(0, 0.02, "noise", 0.40))
# Silence
data.append_array(_generate_tone(0, 0.06, "s", 0.0))
# Number tones (pitch varies per digit)
var num_tones := _get_number_tones(pad_num)
for t in num_tones:
data.append_array(_generate_tone(t[0], t[1], t[2], 0.55))
# "Please" — noise burst + rising saw
data.append_array(_generate_tone(0, 0.03, "noise", 0.40))
data.append_array(_generate_tone(380, 0.10, "saw", 0.55))
data.append_array(_generate_tone(440, 0.10, "saw", 0.55))
data.append_array(_generate_tone(500, 0.10, "saw", 0.55))
return _make_wav(data)
Every SFX in the game uses the same approach: landing thuds, crash explosions, pickup chimes, dropoff fanfares, thrust rumble, the warp melody (a SID-chip-style C major arpeggio), menu jingle, typewriter clicks, and door close sounds. All generated procedurally. Zero audio files required — the MP3 voices are a nice-to-have, not a dependency.
The Story: “The Last Fare”
Space Taxi In Space has 11 cutscenes — one before the game starts, one between each of the 10 levels, and a finale. They’re told Sierra-style: character portraits on the left, dialogue box with typewriter text on the right, C64-blue borders, and a continue prompt.
The arc starts with nostalgia: Tony finds the C64 in his crawlspace, calls his three friends over, and they fire up Space Taxi for the first time in decades. The familiar arguments return instantly — Eric optimizes, Steve narrates, Pierre breaks things, Tony orders pizza.
Then it gets strange. A “WELCOME BACK” message appears between levels that nobody remembers from the original. A level layout matches Tony’s basement — the desk, the shelves, the couch, in exactly the right positions. The score counter hits 247,450 — Eric’s high score from August 12, 1987, recorded in his childhood notebook.
The game, somehow, remembers them.
By the final cutscene, at 2:17 AM, Eric says the quiet part out loud: “I missed this. I missed you guys. I’ve spent thirty years optimizing everything — my schedule, my work, my life. And I never optimized for… this.” Steve defines what Space Taxi is really about: “Someone calls out to you from a platform, and you go to get them. That’s it. Someone calls. You show up.”
The ending: THANKS FOR THE RIDE. SAME TIME NEXT WEEK?
Every cutscene has a flashback mode (sepia-toned grain effect, wood-paneled basement backgrounds) and a present-day mode (dark space, modern desk). The typewriter text plays a procedural click every three characters. You can hold ESC for 0.8 seconds to skip. The story is optional. The gameplay stands alone. But together, they’re the point.
10 Levels, Zero Sprites
Every visual in the game is drawn with _draw() — no sprite sheets, no textures, no image assets of any kind. Platforms are rectangles. The taxi is a polygon. Passengers are stick figures. Stars are circles. Everything redraws every frame.
The 10 levels each have a distinct theme:
| Level | Name | Theme | Mechanics |
|---|---|---|---|
| 1 | Downtown | Steel-blue city platforms | Gentle intro, 2 pads |
| 2 | The Docks | Dark teal, crane structures | Narrow gaps, 3 pads |
| 3 | Uptown | Slate purple, tall buildings | Vertical layout, 4 pads |
| 4 | The Mines | Warm brown cave | Moving rock obstacles |
| 5 | Neon Strip | Animated neon colors | Flickering pads |
| 6 | The Junkyard | Rusty orange-brown | Drifting debris obstacles |
| 7 | Cloud Nine | Light blue, tiny platforms | Wind gusts, low gravity |
| 8 | The Factory | Industrial gray-green | Conveyor belts, fuel stations |
| 9 | Deep Space | Near-black void | Oscillating gravity |
| 10 | Home | Wood brown basement | All mechanics combined, 6 pads |
Each level generates its own background decorations: Downtown has lit windows and neon “TAXI” signs, The Mines has stalactites and gem crystals, Neon Strip has glowing sign outlines and platform halos, Deep Space has distant nebula clouds and asteroid silhouettes, Home has picture frames, a flickering TV, bookshelf lines, and a pizza box.
Drifting ambient space objects float behind the action — satellites with solar panels, lumpy asteroids, comets with trails, and angular space junk. Each level weights the object types differently: Junkyard is full of space junk, Cloud Nine gets comets, Deep Space gets everything.
Level completion triggers a “LEVEL COMPLETE” text built from 5x7 pixel font bitmaps — each character rendered as individual colored blocks that display for two seconds, then explode outward with physics, tumbling and fading.
A CRT overlay adds scanlines and vignette for the retro feel.
The Crash Physics
Crashing without a passenger shows a funny reason (“Blinker fluid leak!”, “GPS said turn left!”, “Turbo encabulator jammed!”) and scatters 45 pixel-block debris pieces that inherit the taxi’s impact velocity, bounce off platforms and screen edges with damping, and fade over 4 seconds.
Crashing with a passenger triggers the full drama: slow-motion (time scale drops to 0.2), camera zoom to 1.4x on the crash point, screen shake, and a ragdoll passenger ejection. The passenger’s body separates into six parts — head, torso, two arms, two legs — each with independent velocity, rotation, gravity, and platform bouncing. They tumble, scatter, bounce off platforms, and eventually come to rest.
While the ragdoll plays out in slow motion, a guilt-trip quote appears center screen:
- “Eric was 3 days from retirement.”
- “Steve never got to finish his coin flip.”
- “Pierre’s last words: ‘Understandable.’”
- “Tony’s pizza is getting cold. Forever.”
There are 24 guilt-trip quotes (6 per character) and 24 funny crash reasons. If a passenger has a costume (10% spawn chance), the costume accessory — party hat, chef hat, cape, fedora — flies off with the ragdoll.
The humor is the specific kind that comes from making you feel terrible about bad driving. You land too hard and a stick figure named after your friend bounces off three platforms in slow motion while the game tells you his portfolio just crashed.
Technical Details
Architecture: 1 .tscn file (root node that bootstraps main.gd). Everything else is pure GDScript — nodes created at runtime, scripts loaded with preload(). The entire game is text files, fully editable by AI coding tools without a visual editor.
State flow: MENU → CUTSCENE → WARP_INTRO → PLAYING → (loop) → ENDING → MENU. The warp intro between levels shows a parallax starfield, the taxi lurching through hyperspace, and random space objects drifting by — planets, aliens, unicorns, space whales, UFOs, space donuts, cats in space helmets, and a lone shoe. Each has a name (like “Moby Drift” or “Sir Floof”) and a deadpan reaction from the taxi (“…it has 7 arms.”, “Don’t make eye contact.”).
Procedural audio for all SFX: Landing, rough landing, crash, pickup chime, dropoff fanfare, thrust loop, level complete arpeggio, menu jingle, gear toggle, warp melody, typewriter click, door close. All generated as 16-bit PCM at 22,050 Hz, wrapped in AudioStreamWAV.
Touch controls: Mobile detection at runtime, on-screen thrust/left/right buttons that appear when needed. Keyboard: UP/W for thrust, LEFT/A and RIGHT/D for lateral. Pause with P, quit with ESC.
Costumes: 10% chance per passenger spawn. Party Steve (party hat), Super Steve (cape), Pimp Eric (purple top hat + gold chain), CEO Eric (top hat + monocle), Chef Pierre (chef hat), Artist Pierre (beret + paintbrush), Smoking Tony (cigarette + smoke wisps), Mob Tony (fedora). Costumes peek above the taxi roof when boarded and fly off during ragdoll crashes.
Continue system: Game Over screen offers “Press ENTER to continue from Level N” if you’ve seen past level 1. High score persists across resets within a session.
Built in a Day with Claude
The entire game was built from scratch in one day with Claude Code (Opus 4.6). No reference code. No sprite sheets. No engine templates. Just a verbal description of a 40-year-old Commodore 64 game and the instruction: rebuild it, but make it about four real people.
Here’s the commit log:
| Time | Commit | What Happened |
|---|---|---|
| 8:57 AM | initial space taxi | 3,199 lines, 17 scripts — complete game with 10 levels, 11 cutscenes, 4 characters, taxi physics, crash debris, procedural audio, CRT overlay |
| 9:43 AM | tweaking gameplay | First playtest — tightened taxi physics, adjusted gravity, collision tuning |
| 6:56 PM | add sounds, real characters | The personality pass — procedural audio engine expanded, 80 idle lines, 8 costumes, MP3 voice pipeline, character system overhaul |
| 7:18 PM | tweaking gameplay | Level balancing, passenger spawn logic, pad detection |
| 7:45 PM | slow mo deaths | Slow-motion crash with passenger (0.2x time, zoom, ragdoll, guilt-trip quotes) |
| 7:51 PM | prep for export | HTML5/Web export, Emscripten config |
| 10:44 PM | fuel, kill passenger, more quotes | Fuel system, passenger collision kills, GUBER rival taxi, expanded quote databases |
| 11:33 PM | controller support | Gamepad support (D-pad, analog stick, shoulders), thrust down, fare persistence through death, half fare on crash |
Notice the nine-hour gap between the morning session and the evening session. The morning was the scaffold — a complete, playable game in a single commit. The evening was the soul — personality, drama, humor, and polish. The first commit was already a game you could play. The last seven commits made it a game about us.
This was Day 2 with Claude. Day 1 was a different game entirely, where we figured out the workflow. Space Taxi was what happened when we stopped learning the tool and started using it.
The Human-AI Loop
Code-only architecture as enabler. The game has exactly one .tscn file — a root node that bootstraps main.gd. Every scene, every node, every game object is created in code:
1
2
3
4
5
var menu_script := load("res://scripts/main_menu.gd")
var menu := Node2D.new()
menu.set_script(menu_script)
add_child(menu)
current_scene = menu
This pattern repeats across the entire game — levels, passengers, pads, HUD, overlays, pause screen, game over screen. The AI reads, writes, and refactors the entire game through text files. No visual editor needed. No binary scene formats to parse. The architecture is the enabler: everything is code, and code is what Claude works with.
Describe the feel, not the implementation. The best results came from describing what something should feel like, not how to build it:
-
“Crashes should feel dramatic when you have a passenger” → produced the entire slow-motion system: time scale drops to 0.2x, camera zooms to 1.4x on the crash point, screen shake at intensity 14, ragdoll passenger ejection with six independent body parts bouncing off platforms, and a guilt-trip quote fading in over the wreckage. About 80 lines across
_start_slowmo_crash(),_on_taxi_crashed(), and the draw/process loop. -
“Passengers should get impatient if you take too long” → produced the GUBER rival taxi system. When the fare timer hits zero, the passenger gets angry (“I’m calling GUBER!”), a rival taxi descends from above with a wobble animation, picks up your passenger, and flies away — taunting you with Monkey Island-style insults like “You fight like a cow” and “Your taxi license came from a cereal box.” Three GUBERs and you’re fired. Over 150 lines with descend/pickup/ascend phases, 15 insults, character-specific rage lines, and a honk SFX.
-
“The voice synthesis should sound like the original C64 but chunkier” → produced the syllable-by-syllable procedural speech synthesizer that builds “Pad 3, please” from noise bursts, saw waves, and square taps at 22,050 Hz, with envelope shaping for each phoneme.
What the human brought. The 80 idle lines. The 24 guilt-trip quotes. The character personalities. The GUBER insults. The cutscene dialogue. The specific humor of 40 years of friendship. “Tony’s pizza is getting cold. Forever.” requires knowing Tony. “Pierre’s last words: ‘Understandable.’” requires knowing Pierre. The AI can write a ragdoll physics system from a one-sentence prompt. It cannot know that Eric actually says “Don’t sell yourself short, kid” or that Steve actually calls coin flips during a crisis.
What the AI brought (in a day). 6,600 lines of GDScript. 10 themed environments with procedural decorations. A full audio engine generating 12 distinct SFX from raw waveforms. Ragdoll physics with per-limb collision. A cutscene engine with typewriter text and sepia flashbacks. Unified input across keyboard, touch, and controller. A rival taxi AI with personality. Scope that would normally take a small team weeks — delivered in a single session.
The controller example as microcosm. The final commit of the day: a human picks up a controller, says “this should work.” Claude examines _setup_input(), extends it with D-pad, analog stick, and shoulder button mappings, adds thrust-down as a new mechanic with a blue top flame, adjusts fuel consumption rates, and wires up Start for pause. One sentence of intent, complete input overhaul:
1
2
3
4
5
6
7
func _setup_input() -> void:
_add_action("thrust_up", KEY_UP, KEY_W, JOY_BUTTON_A, JOY_AXIS_LEFT_Y, -1.0)
_add_action("thrust_down", KEY_DOWN, KEY_S, JOY_BUTTON_INVALID, JOY_AXIS_LEFT_Y, 1.0)
_add_action("thrust_left", KEY_LEFT, KEY_A, JOY_BUTTON_INVALID, JOY_AXIS_LEFT_X, -1.0)
_add_action("thrust_right", KEY_RIGHT, KEY_D, JOY_BUTTON_INVALID, JOY_AXIS_LEFT_X, 1.0)
_add_action("ui_accept_pad", KEY_NONE, KEY_NONE, JOY_BUTTON_A, JOY_AXIS_INVALID, 0.0)
_add_action("ui_pause_pad", KEY_NONE, KEY_NONE, JOY_BUTTON_START, JOY_AXIS_INVALID, 0.0)
Each action gets keyboard keys, a gamepad button, an analog axis, and then D-pad overrides with shoulder buttons for lateral strafe — six lines that unify three input methods. That’s the loop: human says what it should do, AI figures out how.
The Humor
80 idle lines. 24 crash reasons. 24 guilt-trip quotes. 8 costumes. 8 random space objects with 40 names and 40 reactions. Character-specific accessories on portraits (Eric’s glasses and notepad, Steve’s cape, Pierre’s wrench, Tony’s C64 and pizza slice).
The humor isn’t polished comedy. It’s the specific rhythm of how four friends who’ve known each other for 40 years actually talk — stock tips during a space taxi ride, coin flips during a crisis, mild disapproval as a philosophical stance, and pizza as the universal solution. The cutscene dialogue reads like a transcript of an actual evening in a basement, because that’s what it is.
By the Numbers
| Metric | Value |
|---|---|
| Total lines of GDScript | ~6,600 |
| Script files | 17 |
| Scene files (.tscn) | 1 (root only) |
| Levels | 10 |
| Cutscenes | 11 |
| Characters | 4 |
| Idle chatter lines | 80 |
| Crash reasons | 24 |
| Guilt-trip quotes | 24 |
| Costumes | 8 |
| Warp space objects | 8 types, 40 names |
| Procedural SFX | 12 distinct sounds |
| Pre-recorded voice lines | 44 MP3s |
| Input methods | 3 (keyboard, touch, controller) |
| External sprite/image assets | 0 |
| Development time | ~14.5 hours (8:57 AM – 11:33 PM) |
| Git commits | 8 |
| Lines at first commit | 3,199 |
| AI tool | Claude Code (Opus 4.6) |
Play It
Controls: UP/W = Thrust, DOWN/S = Dive, LEFT/A = Steer left, RIGHT/D = Steer right. Controller: A/D-pad Up = Thrust, D-pad/Stick = Steer, Bumpers = Strafe, Start = Pause. Land softly on pads. Pick up passengers. Deliver them. Fly through the ceiling exit. Don’t crash. If you crash, at least crash without a passenger — the guilt is lighter.
Based on Space Taxi (1984) by John Kutcher for the Commodore 64.