Devlog

How We Made Paper Drift: Inside a Godot Mobile Game

Snowcode's devlog: how Paper Drift was made in Godot, with art drawn by code, a deterministic 120 Hz simulation and a validator that flies every level.

By the Paper Drift team at Snowcode Published 7 min read Leer en español

A folded paper plane surrounded by six taped postcards of Paper Drift's worlds: Glacier, Foundry, Jungle, Aliens, Desert and Depot

Paper Drift was made by Snowcode in the Godot engine, with every line written in GDScript. There is no hand-drawn art: every fold, shadow and ink edge is drawn by code. Under the paper sits a deterministic 120 Hz simulation that plays out identically on every phone, and a validator that flies every stretch of the shaft before it ships, to prove it can be beaten.

This devlog is the story of how those pieces fit together in an indie game made in Godot, and what we would tell another team making a mobile game with it.

Why Godot and GDScript

We wanted one small codebase that exports to Android and iPhone, runs its whole test suite headless from the command line, and stays out of the way of a 2D game. Godot 4 does all three. We use the standard build, not the .NET one, and pin the exact engine version, because the editor and the export templates must match.

Two early decisions shaped everything:

  • Scenes are built in code. Our scene files are three-line stubs that attach a script. Layouts are computed at runtime from the screen’s safe area, so there is exactly one copy of every number. A layout checker then opens every screen at four aspect ratios, with Bigger UI on and off, and fails the build if anything leaves the screen.
  • The simulation has no engine types in it. The code that decides where your plane is and what it hits never touches a scene node. That keeps it testable from the command line, and it is the reason the determinism below works at all.

No hand-drawn art: paper drawn by code

A game about paper deserved to look like paper, so we made the look a system rather than a pile of images.

The plane and every obstacle are vector geometry drawn in code, not sprites. Every menu comes from one paper kit: cards are sheets with a faint paper tooth, dialogs carry the creases of a folded dart, group headings are printed on masking tape, tabs are cut from kraft cardboard, quests are tickets with a perforated stub, and their rewards are stamped on. Change the kit and every screen changes with it.

Even the crash is paper. When a run ends, your plane crumples into a ball cut from its own skin: about 27 creased facets, each a scrap of the plane’s own paint turned a different way, so a new skin gets its own crumpled ball with no new code.

A paper plane crumpled into a creased ball above a rooted cactus ball, with the Desert's saguaro below
The crash, drawn from the plane's own paint.

The walls are generated by scripts too, under one strict rule: nothing in the background may look like a beam. A long straight line in a wall reads as something lethal, so the generators never draw one, and no two windows sit at the same height across the shaft. Every real girder carries the same red hazard tape, drawn last, whatever the world’s style. A cardboard girder in the Depot can change its surface, never its outline or its tape.

Paper Drift: made with code
Everything in Paper Drift is code drawing folded paper — and the game flies every level before it ships.

A deterministic 120 Hz simulation

Paper Drift’s physics run at a fixed 120 steps per second, and the picture you see is interpolated between the last two steps. That is why the game looks smooth on a 60 Hz screen, and why a dropped frame reads as a hint of slow motion rather than a jump.

The harder promise is that a run plays out identically on every device. Ghosts in Time Attack are not videos: they are your recorded inputs, re-flown by a second copy of the simulation in lockstep with yours. Daily Drift hands everyone the same course. In Race, both players fly the very same shaft. All of that breaks the moment two phones disagree about a single bit.

Floating-point maths is the enemy here, because trigonometry and powers can round differently on different chips. So inside the simulation:

  • No sine, cosine or power functions. They come from lookup tables generated once and committed to the repository, read with plain addition, subtraction, multiplication and division, which are exact everywhere.
  • No 32-bit vectors. Godot’s standard vectors store 32-bit floats, which would quietly truncate the simulation, so every position is a 64-bit number.
  • No timers that accumulate. A stun is a count of steps, never a float that ticks down.
# Inside the simulation there is no sin(): trig comes from a committed table,
# interpolated with + - * / only, so every phone gets exactly the same bits.
var s := Luts.sin_turns(phase)

A test replays a fixed run and compares a hash of the result with a golden value. If a change moves that hash, it was a gameplay change, and we have to say so.

The level validator: every chunk is flown before it ships

The shaft is endless, but it is built from chunks: short stretches of girders, pistons, fans and shutters. A procedural game lives or dies on whether those chunks are fair, so we never trust our eyes. Every chunk passes a validator that flies it with the game’s real physics, not a copy of them. It checks four things:

  1. It can be flown from anywhere. From every point in the entry gap there must be a way through, not just from the middle. A chunk that works from the centre and kills you from the left wall kills people who did nothing wrong.
  2. It forgives a human. A second pass flies it with a pilot who can only change their mind every 60 ms, with a hitbox 6 % larger.
  3. You get a warning. Every obstacle is on screen for at least 0.9 seconds before the plane can reach it at top speed. To keep that promise we zoom the camera out slightly as you speed up.
  4. The geometry is clean. Nothing pokes more than a few pixels into a wall, and no two obstacles overlap.

It runs all of that for both mirrorings of each chunk, at three speeds from the start of a run to the top speed, and at six different timings of the moving parts, because a piston does not restart its cycle when you arrive. The first version did not finish in ten minutes on a few dozen chunks. Today a full pass takes up to about 24 minutes, and can be split into shards that run in parallel.

No chunk can be cleared without steering

Our favourite bug: early on, the middle of the shaft was always open. The fairness check used the banked plane’s width, but a plane falling straight is much narrower, so the gaps lined up down the centre, and you could clear almost the whole game by never touching the screen. Now the chunk builder refuses to emit any chunk with a straight-down corridor, and two tests guard it. Once it was fixed, a run with no input at all scored 4 points on average and never more than 6: just the free points of the tutorial and the breathers.

Nothing may close the shaft

A shutter that closes completely turns survival into a question of which moment you arrived. So nothing closes: shutter gaps never shrink below 150 px, the guillotine is 420 px wide in a 640 px shaft, and pistons stop 500 px short. There is always a way through.

Two flaming rocks falling in the Foundry while a lane glows at the top of the screen, and a pink paper plane dodging between them
A rock's lane glows at the top of the screen before it falls: every hazard is on show before it can reach you.

Testing the bosses like a player

The six world events that hunt you, from the Depot’s drone to the Aliens’ saucer, cannot be checked by the chunk validator, because they aim at your plane. So they have their own test, which flies every one of them with the real simulation and demands three things. A plain dodging pilot must survive it from anywhere across the shaft, early in a run and at top speed. Holding still must get you hit, or it is scenery with a light show. And every hazard must be on show before it can reach you.

The dodging pilot flies twice: once with a 60 ms reaction lag, and once seeing the screen 200 ms late. That second pilot exists because of a phone. The 60 ms pilot was clearing every boss, and a report from playing on a phone still said “some are impossible to dodge”. A pilot who sees the world a fifth of a second late is a person, and now every boss has to let that pilot through too.

The Aliens' saucer rising below a paper plane in a purple hall, its green tractor beam shining up beside the plane
The saucer's beam pulls you in. The test pilots must escape it too.

Sound and music

The sound effects are synthesised by a script from noise and tones, with no recorded samples anywhere. The music is ours: edited and arranged in-house from tracks first generated with Suno.

What we would tell another indie team

  • Turn every rule into a test. “Every level needs steering” sounded obviously true until we played the game and found a straight line down the middle. Now a test checks it on every chunk.
  • A check that silently skips its work is worse than no check. Our layout checker once reported “no violations” after silently skipping the panels it was meant to inspect. Now a missing panel is itself a failure.
  • Build for the slowest human, not the best bot. A perfect pilot finds a way through anything. The 200 ms pilot is the one that tells you whether the game is fair.

If you want to see the result, the shaft is waiting on your phone. Meet the six hunters in every boss in Paper Drift, tour the eight worlds, or find press assets on our press page.

Questions

What engine is Paper Drift made with?

Godot 4, the standard build, with every line of the game written in GDScript.

Is Paper Drift's art hand-drawn?

No. There is no hand-drawn art in the game. Every fold, shadow and ink edge is drawn by the game's code, and the wall textures are generated by scripts.

How do you know every level can be beaten?

A validator flies every chunk of the shaft with the real simulation before it ships: from every entry position, both mirrorings, at three speeds and six timings, plus a pass that models 60 ms of reaction time and a 6 % larger hitbox.

Who made the music?

The music is ours: edited and arranged in-house from tracks first generated with Suno.

Does a run play out the same on every phone?

Yes. The simulation is deterministic, so the same inputs give the same run on any device. Ghosts, Daily Drift and Race all rely on it.