In this post
The studio is a Flutter studio. Four of five games are Flutter, the rest of the portfolio is Flutter, and the standardisation is deliberate.
One game is Godot 4.3, in GDScript.
This post is about what justifies breaking your own rule.
What the game is
A mobile-first 2.5D platformer in the spirit of unfair-trap games — where the fun is precisely the block that falls where you did not expect. Eleven playable levels across two worlds, a boss on level 10 and a bonus stage.
The word that decides everything in that description is levels.
The criterion: who draws the level?
In Flame or plain widgets the criterion was whether state changes per frame or per event. Here there is a third step, and it is not about rendering:
Is the game's content drawn or generated?
A game with generated content — endless, procedural, random — needs no editor. The rules produce the content, and the work is tuning the rules.
A game with drawn content needs someone placing each element. And placement is visual, iterative work: you move the trap two blocks right, test, move it back, test again. Fifty times per level.
| Content | Tool required |
|---|---|
| Generated by rules | rendering library |
| Drawn by hand | scene editor with live preview |
Without an editor, every position tweak is: change code, recompile, open the game, play to the level, look. With an editor it is drag and look. In a platformer level that happens hundreds of times — the difference is not convenience, it is feasibility.
What Godot provides that was missing
Scene editor. Placing platforms, traps and enemies visually, with the game running next to it. That is the main reason.
Tilemap. Building levels from a block palette, with collision included. Reimplementing that is weeks.
Physics with bodies. A character that falls, slides, pushes and collides with materials has behaviour nobody wants to hand-write — and that must stay consistent across eleven levels.
Node tree with scene inheritance. A trap becomes a reusable scene; changing the base scene updates every instance in every level. That is what makes late adjustments possible.
Fast iteration. GDScript reloads and the game continues. In a game whose fun is the exact timing of a trap, test-cycle speed is the design process.
What it cost
Being honest here matters more than the list of advantages.
Duplicated context. Two languages, two project conventions, two toolchains. Switching has a real mental cost, most visible when returning after weeks.
Different publishing. Export, signing and store submission had automation ready for Flutter. For Godot it was rebuilt. Not hard; time that was not planned.
Bigger binary. A full engine bundled in weighs more than a regular app. In casual games, download size affects installs — that is a product cost, not just a technical one.
Nothing is reusable. Components, theme, utilities, ad integration: none of what existed applied. Starting from zero in a studio that standardised precisely to avoid starting from zero is uncomfortable.
What justifies it anyway
First, the alternative was worse. Building a level editor inside Flutter is a project in itself — and a project that is not the game. Or writing eleven levels in code, which would have turned level design into a programming task, done by the wrong person, at the wrong pace.
Second, platformer is a category, not a project. For a single isolated game the context cost would not pay for itself. Since the format is meant to be repeated, the investment has somewhere to amortise.
Adopting a second stack is justified when it solves a category of problem. Adopting one for a single project is usually a loss dressed up as a technical choice.
The rule that stuck
Written before the next game, so it is not decided in the heat of the moment:
- 1. Content generated by rules → Flutter. With Flame if it changes per frame,
without if it changes per event.
- 2. Content drawn by hand → an engine with an editor.
- 3. Unsure between the two → prototype the mechanic in Flutter first. It is
faster to build, and if the mechanic is not fun, the tool does not matter.
Step 3 is the most valuable and the most skipped. Choosing an engine before knowing whether the game is fun is optimising the execution of an unvalidated idea.
A design note on "mobile-first"
One detail worth recording, because it nearly became a problem: trap platformers were born on keyboards, and keyboard precision does not exist on a touchscreen.
Translating that required changing the design, not just the controls — wider reaction windows on traps, closer checkpoints, cheap deaths and instant restarts. A game of this genre ported without that adjustment becomes unfair in the wrong way: frustrating instead of funny.
No tool solves that. None of them do.
Frequently asked questions
Couldn't the platformer be built with Flame?
It could, at a specific cost: building a level editor, or writing every level in code. Eleven hand-written levels are manageable; adjusting a trap's position in code, recompiling and testing, fifty times per level, is not.
Does Godot export well to mobile?
It exports to Android and iOS. The binary is larger than a regular app and the signing and publishing flow has its own steps — nothing blocking, but it is a different process from the one already automated for Flutter.
Is it worth maintaining two stacks in a small studio?
Only when the second one solves an entire category of problem, not a single project. For one isolated game the context cost would not pay for itself.
Why GDScript and not C#?
Iteration speed. GDScript is the editor's first-class language, with fast reload and direct integration with the node tree. For a game about mechanics rather than raw performance, iteration is worth more than any speed difference.