Why I built my own engine for a one-hour horror game
By Encrypted Cat / 0xKOT · Originally published on itch.io
The phone is four lines of text
How The Cradle’s story drives the whole game, and why I built my own engine for it.
In The Cradle, there’s a moment early on when a phone starts ringing in an empty nurse station. The line has been dead for ten years. You can answer it, or walk away and let it ring.
Players remember that phone. Here’s what it looks like to the game:
{ “type”: “setFlag”, “flag”: “phone_rang” }, { “type”: “phoneRing”, “target”: “prop_phone”, “until”: “interact”, “timeout”: 35 }, { “type”: “wait”, “duration”: 1.5 }, { “type”: “showSubtitle”, “character”: “vetrov”, “text”: { “en”: “A phone? The line’s been dead for ten years.”, “ru”: “Телефон? Линия же мёртвая десять лет.” } }
That’s it. No script attached to the phone, no hidden code behind the scene. A few lines of plain text, and the engine does the rest: it finds the phone in the building, rings it, waits for you, and has Vetrov say his line, in your language, with his voice or as a subtitle.
A game that is a script
Most games are built the way a movie set is built: the logic lives inside the scene. A door has a script, a monster has a script, a trigger box has a script, and the story is scattered across hundreds of little pieces attached to objects.
The Cradle works more like a theatre. There are three separate things:
- The stage is the building: a sanatorium and a radio station, generated by code in Blender. Every door, lamp, bed and phone has a name.
- The script is the story: one file,
story.json, that says what happens, when and why. - The engine is the crew that performs it. It reads the script, and when it says “ring prop_phone”, the phone rings.
The script for Episode 1 is about 440 KB of text. It holds the whole game:
Sequences (things that happen, step by step)
70
Triggers (places in the building that start something)
71
Things you can interact with
116
Documents to read
21
Characters with voices
12
Hunters (things that look for you)
6
Achievements
16
It uses 77 different kinds of steps: play a sound, show a subtitle, open a door, change a light, move an actor, start a chase, end the episode… The most common one is wait, used 290 times. Horror, it turns out, is mostly waiting.
Even the monster is text
Here’s the Nanny, the thing that walks the station corridors:
{ “id”: “nanny”, “preset”: “stalker”, “when”: { “all”: [ { “flag”: “sleeper_awake” }, { “not”: { “flag”: “lift_down” } } ] }, “zone”: [“zone_station”], “avoid”: [“zone_transmitter”], “patrol”: [“spot_nanny_archive”, “spot_nanny_duty”, “spot_nanny_quarters”, ”…”] }
There’s no behaviour script here. The engine already knows how a hunter thinks: it sees by light, it hears by sound, it checks hiding places, it gives up and wanders off. The story only says who she is, where she can go, where she can’t, and when she wakes up. How frightening she is lives in the engine; what she means lives in the script.\
Why that’s different
Keeping the whole story in one readable file sounds like a small decision. It changes almost everything.
The game can check itself. Before an episode ever reaches a player, the engine reads the script end to end and asks questions:
- Does every door the story opens exist in the building?
- Does every sound exist?
- Does every voiced line have a recording?
- Can the player actually walk to everything the story asks them to reach?
- Does a hunter’s patrol stay on floor it can walk?
- Are the scares paced, or are three of them stacked in the same thirty seconds?
A broken story fails to load, the way a typo fails to compile.
One file feeds everything else. The same script is read by:
- the voice tool, which knows exactly which lines to record and for whom;
- the subtitles and the dialogue log;
- the achievements (“read all 21 documents” is just a rule over the story’s own flags);
- the chapter select, which can drop you at any checkpoint with the right items in your pockets.
Nobody has to keep these in sync by hand. They read the same script.
Time can be bent. Because the whole story is data, the engine can simulate a playthrough without playing it. That’s how “skip to chapter 4” knows which doors should already be open and which flags are set, without anyone walking there.
The story is written like a story. I don’t write the JSON by hand. I write it in a small story language, in a Python file of about 4,000 lines, where a radio call looks like a radio call, and a script turns it into JSON. When I want to change what Gromov says on the radio, I change one line and rebuild.
Fear has a shape
A one-hour horror game lives or dies on pacing. Too many scares in a row and players go numb. Ten quiet minutes and they get bored, and on stream, chat gets bored first.
Because the story is data, the engine can draw the game’s fear curve before anyone plays it.

Scare curve graph
Every scare in the script is a beat. The engine works most of them out on its own: a jumpscare is an 8 out of 10, a hunter starting her patrol is a 6, a figure that moves while you’re not looking is a 4, a flickering light is a 2. Where it guesses wrong, I tag the step myself.
Then it plays the whole story without playing it, the same simulated playthrough the chapter select uses, and puts every beat on a clock: how long it takes to walk between rooms on the real map, how long each voiced line lasts, how long each scene makes you wait. That’s the chart above: chapters along the bottom, fear up the side, every scare a dot.
And it complains, like the other checks do:
- more than four minutes with nothing scarier than a creak, and no monster loose;
- two big scares within thirty seconds;
- the same trick three times;
- a big scare the player might never see.
Some of those are on purpose. The Listeners move when you look away, again and again, because that’s who they are. So that warning is switched off in the episode’s settings, with the reason written next to it. Another one reads “the tape is the tension”: a quiet stretch where a cassette recording does the scaring.
The real curve is longer than the chart. A first-time player searches, reads every note and dies a few times, so I tune the clock to a 45-minute run and read every quiet gap as “at least this long”.\
Why I built my own
Not because other engines couldn’t. Unity or Godot could have made this game.
I’ve been a software engineer for over 15 years, mostly building enterprise applications, but game development is new to me. It’s something I’ve always wanted to do. I’m also a small streamer, and my favourite nights have always been horror nights with chat. So I wanted to make my own short horror games, about an hour each, that I could play on stream, share with other streamers, and that anyone could open in a browser tab from one link. Building the engine myself was how I wanted to learn what’s really going on inside a game.
I built it with a lot of help from AI coding tools: much of the code was written by AI. The architecture, the specs, the reviews and the testing were mine, and a lot of that testing happened on real phones, tablets and a Steam Deck, guided by players’ feedback.
What it costs
It isn’t free:
- No visual editor, apart from a cutscene timeline I built into the game itself.
- Every tool is mine to make: the map builder, the voice pipeline, the testing robot that plays the game in a muted browser.
- Every bug is mine to find. The project keeps a long list of the bugs it has shipped, and the rule each one taught. Some are funny: for a while every chair in the building faced south.
But when a streamer’s chat finds the phone and screams, I know exactly which four lines made that happen.
What’s next
Episode 2 will be a new script on the same stage crew, with new rooms, new voices and a new thing in the dark. The engine already knows how to be scary. The story just has to tell it what to be scared of.
Play The Cradle free in your browser: https://0xkot.itch.io/the-cradle-episode-01
Discord: https://discord.gg/ufYtrP3NRk
Reddit: https://www.reddit.com/r/TheCradleHorror/
- 0xKOT, Encrypted Cat Games