← Back to blog

I Built an Open-World Copenhagen With Claude Code

TL;DR
  • Grand Tour Copenhagen is the real Copenhagen in your browser. Drive, bike or walk today's city, then press Y for 1760.
  • It runs on Lasse Saari's open-source worldhood engine, with houses from Københavns Kommune's open 3D city model.
  • I built it with Claude Code in about a week, over 300 commits from the data scripts to the trailer.
A loop from the trailer. The full video is on YouTube.

The short version

Grand Tour Copenhagen is the real Copenhagen in a browser tab. You drive, bike or walk around today's city, and when you press Y you're standing on the same spot in 1760.

You can play it at gtcopenhagen.com. It works on a phone, but a laptop is better.

It's built on worldhood, an open-source city engine made by Lasse Saari. He did Helsinki first. I forked his engine on 12 October and added Copenhagen.

I built it with Claude Code, and I used it at every step: the game code, the data scripts, the 3D models, the checks and the trailer. That came to over 300 commits in about a week. When I write "we" below, that's me and Claude Code.

This is the long version, with screenshots of what broke.

How it started

It started with a LinkedIn post. Lasse shared GTA Helsinki, a version of Helsinki you could drive around in your browser. I live in Copenhagen, so on the morning of 12 October I started on one for here, with a few twists so it wouldn't be a copy.

Half an hour later I had a three.js game called Grand Theft Ladcykel (a ladcykel is a Danish cargo bike). It was Copenhagen built from scratch on OpenStreetMap, and by 10:10 it was live on my Cloudflare account. The main twist was a switch that takes you back to historical Copenhagen, with old vehicles and buildings that aren't there anymore.

The first version, Grand Theft Ladcykel: a cyclist on a purple GPS line between colourful houses
The first version on 12 October, built from scratch. The card at the top is a delivery job: smørrebrød for the Opera.

By the afternoon it had about 8,800 buildings, bikes, cargo bikes, cars, GoBoats and police that came after you. It also had six sagas from Danish folklore, a morning paper written from what you did the day before, and title art I made with OpenAI.

It still felt clunky. The streets were empty and some of the buildings looked bad.

Lasse's version looked a lot more real and full. His engine, worldhood, is MIT-licensed, built so a new city can be added with a couple of commands, and much smoother to drive. So that afternoon I tried Copenhagen in it.

Lasse's Helsinki in worldhood: a car on a wide street near the harbour terminal
Lasse's Helsinki in worldhood.

I forked it at 16:44 and brought my extras across: the 1760 switch, the sagas, the morning paper, the cargo bike, delivery jobs and the GPS line.

The name came later. I wanted Copenhagen in it, but GTA is a Take-Two trademark, so it became Grand Tour Copenhagen. I also kept the fork's own look instead of copying other city games.

The first evening on worldhood: a white car and a police car on a flat grey street
Copenhagen on Lasse's engine, the first evening.

The setup

I worked with Claude Code in a cloud session: a Linux machine with the repo checked out, a dev server and a headless browser for screenshots.

The code lives on GitHub. Every push to the main branch starts a build on Cloudflare, and about two and a half minutes later the new version is live. The game is only static files (HTML, JavaScript, 3D models and city data), so hosting it costs nothing.

We kept three copies of the code side by side, called git worktrees. A long job could run in one while a quick fix went out from another.

A normal day went like this. I tested the latest build and decided what to fix or add next. Then I built it with Claude Code, checked the result and pushed it, and tested again.

A diagram of the loop: I test the live build, decide what's next, build it with Claude Code, run tests, screenshots and checks, push to GitHub, and Cloudflare deploys it in about 2.5 minutes
How a change got to the live site.

When a check failed, the change went back for another round before it was pushed.

At night I set it up to keep going without me, working through a task list on its own.

The first run lasted 8 hours and the next one 14. After that it became one continuous session that checked in on itself on a schedule and pushed every batch that passed its checks. On the last two days I ran a second session alongside it for the trailer and the crowds.

A bar chart of commits per day from 12 to 20 October 2026, split into midnight to 7:00 and the rest of the day; 89 of 315 commits landed at night
From the git log of the game repo. 20 October counts up to the morning.

Today's Copenhagen, from open data

Almost nothing in the city is placed by hand. Scripts build it from open data, and the game reads the result. The map is a circle 4.4 km across, centred on the old town.

What you seeWhere it comes fromHow many
HousesKøbenhavns Kommune's 3D city model from 2013, plus OpenStreetMap for newer buildings20,744
Street treesThe city's street-tree registerAbout 21,000 in the register
Lamps, signs, poles, bike racks, benches, binsThe city's registers, plus objects Mapillary spotted in street photos66,944 in the data, before duplicates are dropped
Bicycle symbols on the cycle tracksCycle tracks in OpenStreetMap, one symbol every 40 m6,690
Parked carsA script that looks for free kerb space3,540
Boat mooringsA script that walks every quay looking for open water1,261 spots

Streets, paths, water and parks come from OpenStreetMap. The public servers kept timing out, so we downloaded the whole city as one file (a BBBike extract) and built from that.

Helsinki has a textured 3D model of the whole city. Copenhagen doesn't have one like it, so I went looking and found the municipality's model from 2013.

Each city block in it is one big mesh. A script cuts it into separate houses along the OpenStreetMap outlines, roof by roof and wall by wall. The houses keep the heights the city measured.

Nyhavn today in the game: the car on the quay road, coloured houses and boats on the canal
Nyhavn today, from the trailer.

What you see and what you bump into are two different things. The car collides with building footprints, not with the 3D model, so the two have to match. For a while they didn't, and you could drive straight through the rows of houses in Nyboder.

OpenStreetMap often draws pavements as lines, or not at all. So a script paves everything within 5 m of a road that isn't a house or water. That added 3.4 km² of pavement.

Nyhavn got special treatment. We went through street-level photos on Mapillary and wrote down the colour of each facade along the north quay, 30 houses in order.

Copenhagen in 1760

The same aerial view of the city twice, labelled Today and 1760
The same camera over the city, today and in 1760.

1760 isn't a separate map. It's today's map run through a script that takes the modern city away and puts the old one back, using the city's own register of building years.

Inside the old walls the streets haven't moved much since then. So the street plan and the plots stay, and the modern blocks get restyled as period houses.

Outside the walls, a house only stays if the city's SAVE register dates it to 1760 or earlier. The register lists 31,018 buildings. Everything else becomes open fields, with the old country roads leading out of the city gates.

The western and northern ramparts are rebuilt as earthworks with bastions and a moat, and a windmill stands on each of the 12 bastions. You can still trace them today. Vester, Nørre and Øster Voldgade run along the inside, and the lakes in Ørstedsparken, Botanisk Have and Østre Anlæg are what's left of the moat.

A few landmarks that are gone today were built by hand: the old Gothic Vor Frue Kirke and the spire of Nikolaj Kirke. The Marble Church is a building site, because in 1760 it was only half built.

Taking things away was harder than it sounds. Modern footprints with no house drawn on them were still solid. For a while the Opera, BLOX, the Black Diamond and 140 other buildings were invisible walls in the middle of open squares.

There are no trams or buses. Your car becomes a coach pulled by two horses, with a coachman in a tricorne up front. There are lanterns instead of street lamps, market stalls, pigs and hens, barrels and crates on the quays and tall ships in the harbour.

Nyhavn in 1760: two horses pulling a coach along the cobbled quay
Nyhavn in 1760, with the coach and its two horses.

The men in 1760 wear 18th-century costumes I picked out, and the player gets a blue coat. Half the squares have a night watchman and most have a woman spinning at her distaff. The background music is generated folk tunes, played on a cittern.

Six figures in long 18th-century coats in red, blue, brown and green, with tricornes
One avatar, six 18th-century coats.

Making it feel like Copenhagen

Empty cans lie by about a quarter of the benches, a third of the bins and nearly half the bus shelters, plus two at every hot-dog stand. Each one is worth its 1 kr deposit, like the real thing. A can you pick up comes back five minutes later, and you get a title at 10, 50, 150 and 400 cans.

The can itself is an artist's model, repainted as a lemon-lime soda with no name or logo.

There are five hot-dog stands: two on Rådhuspladsen, and one each on Kongens Nytorv, Amagertorv and at Torvehallerne.

The harbour bath at Islands Brygge has its decks, its diving tower and people swimming. Geese graze in groups of three to seven in the big parks and waddle off when you get within 5 m. Some walkers have a dog on a lead, and a third of the empty bike racks have a cargo bike parked at them.

Hidden nisser, the house gnomes of Danish folklore, sit around the city for you to find. Their boss, Nissefar, lives in Kongens Have and wants porridge.

There are six sagas, short stories from Danish history and folklore. Andersen's Three Homes and Gefion's Plough play today, and Porridge for the Nisser and Holger Danske's Map run in both eras.

The other two only exist in 1760: The King's Eye, which a ghost gives you, and Fire Watch, a night shift with the watchman.

The city follows the calendar. From 10 October to 2 November there are carved pumpkins on the doorsteps and café tables and orange lights over Nyhavn. From 20 November to 6 January it's Christmas trees on the main squares, lights over the shopping streets and falling snow.

The Christmas shot twice: a huge flat tree filling the screen, then a lit tree with a star on the square
The Christmas shot: the first try, with a tree that filled the screen, and now.

The car has a radio with five stations. They play music that's in the public domain or under open licences, streamed from the Internet Archive and Wikimedia Commons.

The radio card in the game: station Hygge Folk, playing Dala hambo, recorded in 1919 by Lager and Olson
The radio card. This hambo from 1919 has since come off, along with the other crackly gramophone records.

From stick figures to people

The people started as simple figures drawn in code. Up close they were blocky stick figures, and in 1760 some of them also floated above the ground.

So we went looking for character models. They started as free Quaternius characters, and later a Ready Player Me avatar I found joined them. There are 29 in the game now, from a guy in a hoodie to sailors, a shipboy and a woman in a Victorian dress.

Each family of models shares one skeleton, so a few sets of walk, run and idle animations move the whole crowd. Only the ten people nearest the camera (five on a phone) and the player are fully animated. Everyone else out to 140 m uses the same models baked into 12 animation frames, so 80 people cost about a dozen draw calls.

A grid of simple 3D models built in Blender: a coach, a horse, a goose, a pig, a hen, a harbour bus, a cargo bike box, a bike, a scooter, a bus shelter and two ships
Some of the 31 models we built in Blender. The coach, the horse and the animals were later swapped for models I found.

For 1760, I dressed that avatar in the 18th-century costumes I'd found. They have long coats in red, blue, brown and green, a waistcoat, breeches, stockings and a cravat, plus powdered hair and a tricorne. The coachman is the same man, sitting down.

H.C. Andersen is an easter egg I added on day three. He stands outside Nyhavn 20, and yes, he can chase you.

He got his own build, with a black coat, a white cravat and a top hat. In the first preview, his top hat came out as a flat disc sticking out sideways. The 3D library builds cylinders lying down, and nobody had stood it up.

His coat was jagged until the model got more detail. He also stood with his feet wide apart, because the standing animation was made for a different body. And at first he stood inside his own front door instead of on the pavement outside it.

H.C. Andersen three times: a first preview with a flared coat, standing inside his doorway, and standing on the pavement beside it
H.C. Andersen: the first preview, standing in his doorway, and now.

The failures, with screenshots

Most of the week went into fixing things that looked wrong. A few were funny. Here are the best ones, in order.

Flat walls, windows on the inside

Early Copenhagen in the game: a white car between flat, featureless walls
13 October. Cars glitching into flat, empty walls.

The municipality's model has no textures, so every wall started out as one flat colour. About 45% of the model is also built inside-out, so on those houses the windows sat inside the walls until we flipped them.

A box for a coach

The first 1760 version: a coach drawn as a black box, block-shaped horses and a person lying in the road
13 October. The coach was a black box, the horses were blocks, and someone was lying in the road.

That first coach was a box drawn in code. Next we built one in Blender, and in the end it became a 19th-century coach and a horse model I found, with a coachman on top.

Poles in the lanes, and a forest of signs

Two screenshots: tall poles standing in the driving lanes, and a street crowded with sign posts
13 and 14 October.

Some of the street furniture comes from Mapillary, which spots objects in street photos. It sees the same sign in many photos, so we got forests of signs, wooden utility poles and doubled traffic lights. Cleaning up the duplicates removed 7,483 of 17,633 sign posts, 1,488 poles and 606 traffic-light heads.

The poles standing in the lanes were a separate bug, fixed on the first morning.

The car in the canal at Nyhavn

The car driving on water beside the Nyhavn quay, photographed off a monitor
17 October, photographed off my screen. I'd first spotted the problem on 14 October.

The ground near the water had sunk by up to 2 m, so parts of the quay ended up under the water. It took until 18 October to fix it properly.

A coach on its side

The 1760 coach tipped over on a buckled quay
18 October, on the causeway at Nørreport, where the ground had buckled.

The code that levels the ground also lifted single points by up to 2 m, which put spikes into squares like Højbro Plads.

Driving on water in 1760

The 1760 coach driving on the canal under a dark bridge
18 October. The coach on the canal, under a bridge it should have been driving over.

In 1760, 79 of the 90 bridges dropped you into the water. Today's city had the same problem on 120 of 261.

The Little Mermaid, airborne

A dark object floating above the harbour with a person sitting on it
18 October. The Little Mermaid on her rock, floating over the water at Langelinie.

By that afternoon she was back down on her rock.

Knippelsbro from above

Knippelsbro seen from above before and after the fix: a patchy deck, then an even one
Before and after, from the night of 18 October.

Paving that lies over water gets thrown away, so the canals don't get paved over. That rule also caught the bridge decks, so the deck showed through in patches. Now the paving stays wherever there's a bridge deck under it.

The rest of the list

DateWhat it looked likeWhy
13 OctBoats floating 4 m above Nyhavn, three-masters in the LakesBoats ignored the water level and the kind of water
13 OctGranite kerbs lying across roadsKerbs were only checked at the middle of each edge
13 OctNo street surfaces on the live siteCommas in file names sent Cloudflare's file server into a loop
13 to 18 OctPolice cars, bridge decks, walkers and two statues went missingFive times, a // comment in a one-line statement switched off the rest of the line
14 OctThe live game failed to start for 10 to 15 minutes after a push at 01:00The harbour bath asked where its diving tower was before the tower existed
15 OctFlickering grey and red wallsAbout 400 buildings are in the model twice
17 OctHollow buildings with walls hanging in the airTheir lower floors were never modelled. Walls now reach the ground on 5,302 buildings today and 1,511 in 1760
18 OctGrey stripes on the coach wheelsThe texture was read upside down
18 OctPumpkins in every trailer shotDecorations follow the date, and it was October
19 OctNisser standing on road crossings and in the waterTheir spots didn't check for roads and water
19 OctA wall of trees on RådhuspladsenThe tree register lists some trees twice, so 77 trees stood within 40 m. Now it's 29
19 OctBricks in the roadLeftover kerb pieces from small scraps of pavement
19 OctBlack squares all over 1760One broken pixel (a NaN, "not a number") spread through the blur effects
19 OctAfter a crash, the driver got out of a car that was still rolling, and his door stayed standing in the roadHe got out before the car had stopped

How we checked the work

I couldn't play every build myself, and Claude Code can't see the game the way I do. So a lot of the work was building checks.

The cloud machine has 2 CPUs, 8 GB of memory and no graphics card. It draws the game in software inside a headless browser, and starting it once took 5 to 8 minutes. It's slow, but it meant we could take screenshots from any camera position.

For the trailer, a script holds every shot near its start, middle and end. It puts the frames side by side on one image, a contact sheet. One sheet covers every shot in the trailer, so a broken one jumps out.

A contact sheet: rows of trailer frames, three per shot
A contact sheet from the night of 18 October. Each row is one shot, near its start, middle and end.

Some bugs are easier to find with numbers than with pictures. To find floating people, a script fires a ray straight down from every walker and reports anyone whose feet don't touch the ground.

Other checks go over the whole city. Rays from inside every house look for walls you can see through. Another check looks for houses you can drive through and walls you hit but can't see, and another for bridge decks with no ground under them.

One check looks for steps and spikes in the ground under streets and pavements. Before the fix it found 609 spots under streets where the ground jumped by more than a metre. After the fix, 151.

There are about 750 automated tests, and they had to pass before every push, along with a smoke test that starts the game. Sub-agents also reviewed the code 11 times.

We hit every limit the machine had. Two headless browsers at once ran it out of memory, and the browser got killed.

The dev server died after two hours and had to be restarted between runs. Once the whole machine restarted in the middle of the night, and the session picked up where it had stopped.

None of this replaces playing it. I still found things the checks missed, like the striped wheels on the coach.

Shipping it

On 17 October I heard someone had already made Oslo, and I didn't want anyone to beat me to Copenhagen.

That's when I pushed for launch: the new name and a cinematic mode for the trailer. The download had already got lighter overnight, with the city data cut from 10.2 MB to 3.1 MB.

For one night, every commit on GitHub had a red cross from Cloudflare next to it. It looked like the site was broken, but it wasn't. We were also pushing a second branch, and Cloudflare built it as a preview with a command that refused to run without a setting we didn't have.

GitHub only showed that red check, which hid the green production deploy. I pulled the build log from Cloudflare, and with it the fix was one empty block in the config.

On 19 October I bought gtcopenhagen.com from Cloudflare's own registrar. It costs $10.46 a year, with no markup and the same price on renewal. Attaching it to the game took two clicks, and HTTPS came with it.

Hosting is free, so the domain is the only bill for the game itself.

The cloud machine couldn't record video without a graphics card. So in cinematic mode the game drives itself through the shots, and I recorded my screen on my Mac.

Add ?cinematic to the address and it plays the scripted shots. There's the car through the city, slow orbits around landmarks, Halloween at Nyhavn and the Christmas tree on Kongens Nytorv. In 1760 most of the same shots run with the coach.

Title cards from the trailer: Grand Tour Copenhagen, The real Copenhagen, Press Y and 1760
Title cards from the trailer.

The trailer needed its own debugging. The car snapped sideways at junctions, shots started before the character models had loaded, and sometimes the car wasn't drawn where the camera was looking.

What I learned, and what's next

Claude Code made me fast at every step. What it didn't replace was deciding what to build, what good looks like, and when something wasn't there yet. That still takes someone who knows what Copenhagen is supposed to look like.

I stopped trusting "fixed" on its own. Every change had to come with a screenshot, a number or a test before I believed it. The checks caught far more than I would have by playing alone.

Without Lasse's engine I'd still be on version one. Without the municipality's 3D model and registers, there would be no city to drive around in.

Next is making it right street by street. If you live in Copenhagen, find your street and tell me what's wrong or missing. I'm also looking for more characters, like people in winter coats for December.

Credits

Of the 96 3D models in the game, about 50 started as models I downloaded, mostly from Sketchfab. We built 31 in Blender, and 14 are free Quaternius characters and animations from the first version.

  • Engine: worldhood by Lasse Saari and contributors, MIT licence.
  • Map data: © OpenStreetMap contributors, ODbL.
  • City data: Københavns Kommune, CC BY 4.0: the 3D city model (2013), street trees, street lights, bike racks, bollards, wires and masts, and the SAVE register of building years.
  • Street-level photos and detected street furniture: Mapillary (photos CC BY-SA 4.0, map features under Mapillary's terms).
  • Elevation: Mapterhorn terrain tiles.
  • Radio: music in the public domain or under CC0, CC BY and CC BY-SA, from the Internet Archive and Wikimedia Commons.
  • People (CC BY 4.0 unless noted): characters by Quaternius (CC0); "Cool Man" by ardhanaputra and "Avatar Full body - Ready Player Me - pmariano" by Patricio Mariano (Ready Player Me avatars, non-commercial use under Ready Player Me's terms); "Woman Dress 2" by Ali Şahan Yalçın; "Victorian Woman dress 3d model" by abauerenator; the merchant's wife and the spinner by the Department of Historical Information Science, MSU; "Royal Navy Shipboy" by JanPohl; "Pirate Crew - 3D Character Pack" by Itom; the night watchman by augustusburg_ger; the 18th-century coats from costumes by the IVSPU Department of clothes pattern design (Victor Kuzmichev, Aleksei Moskvin, Mariia Moskvina) and by Aleksei Moskvin.
  • Animals (CC BY 4.0): "Goose Low Poly" by Atlas, "dog" by neighbor, "lowpoly pig" by fabiotambone, "Chicken" by pooiloui2 and "Armored Horse" by naminoff.
  • Vehicles and boats (CC BY 4.0): "19th Century Carriage" by Digital Goblin; "Horse Drawn Carriage 16" and "9" and "Merchant Schooner 3" by agt14032013; "Day 249: Babboe cargo bike" by alban; the frigate Svjatoi Nikolai and the schooner yacht Amphion by Museovirasto (Finnish Heritage Agency); "Weathered 18th Century Galleon" by Galaxy Abundant; HDMS Gorm by martyn169.
  • Props and statues (CC BY 4.0 unless noted): the Frederik V statue by alexabhisak; the H.C. Andersen statue by rigsters; "Giant Hot Dog Stand" by Ariel Stull; "City Trash Can" by _Tempus_; "Cans!" by Tiago Oliveira; pumpkins by monsieurmo, Nick Kotik and Charlie Tinley; "Pirate Cannon" by maxwellmccurry; "Barrels" by RipeR; "Crate" by SomeKevin; "Elizabeth's Food Stand" by bpcrews; "Gnome (Female) (Pig Tails)" by David Wigforss (CC BY-NC 4.0, non-commercial use).
  • Trees: "Realistic Tree" by Daniel (danielpetrov) and "Tree GN" by TechArtBGN (CC BY 4.0), plus procedural trees from EZ-Tree by Daniel Greenheck (MIT).

The game's sources panel lists every model and dataset, with where it came from.

FAQ

Can I play Grand Tour Copenhagen?
Yes, at gtcopenhagen.com. It runs in the browser with no account. It works on a phone, but a laptop is better.
Did you code it yourself?
I built it with Claude Code and used it at every step, from the data scripts and 3D models to the tests and the trailer. I decided what went in and when it was good enough.
Where does the city data come from?
Streets, paths and water come from OpenStreetMap. The houses come from Københavns Kommune's 3D city model, and the trees, lamps and bike racks from the city's open data. The 1760 version uses the city's SAVE register of building years.

Sources