Inspiration
Stonenine started as one of those ideas that refuses to disappear.
I had been carrying the core concept around for years: what if a block puzzle remembered every move you made? Instead of blocks simply occupying space until they were cleared, I imagined a board where every piece was slowly running out of time.
I never actually built it.
Then Shipaton happened.
It gave me the push to stop treating Stonenine as an idea and start treating it as a real product. Over roughly three months, my girlfriend and I turned that concept into a complete cross-platform game.
The result is Stonenine, a strategic survival block puzzle built around one simple rule:
Every block ages.
Blocks have health from 9 down to 1. When you make a move without clearing a line, existing blocks lose health. Eventually they turn into permanent Stone obstacles that can prevent entire rows or columns from being cleared.
That small change completely transforms the familiar block-puzzle formula.
You are not only asking:
"Where can I fit this piece?"
You are also asking:
"Which blocks are about to die?"
"Can I create a line before that happens?"
"Should I protect this part of the board?"
"Do I spend a superpower now, or save it for later?"
Clearing lines heals surviving blocks, creating a constant tension between decay and recovery. Around that core loop we built power-ups, superpowers, combos, progression, an in-game economy, Adventure levels, and an endless Classic mode.
What started as a tiny game mechanic became a surprisingly deep strategy system.
How we built it
One of my biggest goals was to avoid building three separate versions of the same game.
Stonenine is therefore built with 100% Kotlin Multiplatform and Compose Multiplatform, sharing the application and game code across Android, iOS, and Desktop JVM.
There is no XML-based UI layer. The interface, game board, menus, animations, dialogs, store, progression system, and visual effects are all written in Compose.
Underneath the game we built a modern KMP architecture using:
- Kotlin Multiplatform + Compose Multiplatform
- Metro compile-time dependency injection
- Jetpack Navigation 3
- DataStore Preferences with platform-specific factories
- Firebase Remote Config for live game balancing
- RevenueCat KMP for purchases and the in-game coin economy
- Cross-platform advertising integration
- Roborazzi visual regression testing
RevenueCat powers our real-money coin purchases, while coins can then be used inside the game for themes and gameplay superpowers.
The game itself runs on an 8×8 aging grid. Every living block has a health value in the range 1..9 and the board evolves after every placement.
Newly placed pieces create a one-tile protection zone, line clears trigger a healing loop, blocks that reach zero health become Stone, and Stone blocks can no longer participate in ordinary line clears.
On top of that system we built 7 gameplay power-ups and 6 player-controlled superpowers, including rotation, rerolling, board crushing, rewinding moves, destroying Stone blocks, and resurrecting an aging board.
The piece generator also reacts to the state of the board. When the grid becomes crowded, smaller pieces become more likely and heavy pieces are suppressed. We added an anti-softlock fallback so that when the current hand cannot fit anywhere, the generator can recover with a playable shape instead of ending a run unfairly.
Building for performance
One of the most interesting parts of the project was discovering how different a game is from a normal Compose application.
A settings screen can tolerate a little unnecessary work.
A board being dragged, animated, highlighted, healed, damaged, exploded, and redrawn continuously cannot.
We spent a significant amount of time profiling and optimizing the rendering path. The board uses performance-focused Canvas drawing, graphics-phase work where possible, and Compose's fast collection operations such as fastForEach and fastMap inside hot rendering paths.
The goal was simple: interactions should feel immediate and animations should remain fluid on both 60 Hz and 120 Hz displays.
That forced me to learn much more deeply how Compose actually performs recomposition, measurement, layout, and drawing, rather than simply knowing how to make a UI appear on screen.
Testing a highly visual game
Stonenine contains a lot of visual states.
There are multiple screens, dialogs, board states, themes, power-ups, HUD elements, store components, progression screens, and responsive layouts. Manually checking all of them every time something changed quickly became unrealistic.
So we built our own automated visual-regression workflow.
A reflection-based SnapshotScanner discovers functions marked with our snapshot annotations and automatically captures Roborazzi baseline images across Robolectric and Desktop JVM tests.
That means UI changes can be compared visually instead of depending entirely on manual testing.
For a project where a few pixels, an incorrect animation state, or a broken layout can dramatically change how polished the game feels, this became one of our most useful development tools.
Security and anti-cheat
Because Stonenine has progression, currency, purchases, high scores, and inventory, we also wanted local game state to be harder to manipulate than a normal preferences file.
We implemented several defensive layers.
Persistent game data can be protected with salted HMAC-SHA256 integrity signatures. Important in-memory numerical values can use randomized XOR masks and inverted checksums through ObfuscatedInt and ObfuscatedLong, making simple memory scanning more difficult.
On Android we additionally implemented checks around debugging, root environments, and package-signature integrity.
None of these techniques make a client device magically trustworthy, but building them taught us a great deal about thinking defensively instead of assuming that local state will remain untouched.
What we learned
Before Stonenine, I knew Kotlin and Compose.
Building an actual game with them was completely different.
I learned how to create responsive game interactions, build animation systems, optimize Canvas rendering, manage complex state without destroying performance, and design mechanics where many small systems interact with one another.
I learned that game development is often less about writing one complicated algorithm and more about making dozens of systems behave correctly at the same time.
A power-up affects the board.
The board affects scoring.
Scoring affects progression.
Progression unlocks new mechanics.
Those mechanics change generation probabilities.
All of it must survive app restarts, work across platforms, animate correctly, and still feel understandable to the player.
I also learned about an entirely different side of shipping software that is easy to ignore while you are programming.
We had to think about purchases, privacy, terms of use, platform requirements, data handling, and European compliance. I wrote our privacy policy and terms and worked through the compliance requirements necessary to turn a personal project into something that could actually be distributed to real users.
That process changed how I think about software.
Building the feature is only part of building the product.
Challenges
Our biggest challenge was balancing complexity with clarity.
Stonenine has aging blocks, healing, Stone formation, spatial protection, power-ups, superpowers, combos, progression, inventory, coins, adaptive piece generation, and multiple game modes.
It would have been very easy to build something technically interesting but impossible to understand.
We repeatedly simplified the interface, changed animations, adjusted timing, reworked visual feedback, and tuned mechanics until the player could see what the game was doing instead of needing to read an explanation.
Performance was another major challenge.
Compose Multiplatform gave us an incredible amount of shared code, but a game stresses a UI framework very differently from a conventional application. Drag interactions, board previews, simultaneous tile effects, score animations, and continuous state changes exposed inefficient code immediately.
We had to learn where allocations mattered, where recompositions mattered, what belonged in the graphics phase, and when an elegant abstraction was too expensive for a hot rendering loop.
Cross-platform development created its own challenges too. Android, iOS, and Desktop share almost all of the game's logic, but purchases, security, storage, advertising, and platform lifecycle behavior still have platform-specific edges.
Solving those differences without allowing the shared architecture to collapse into platform-specific branches became one of the most valuable lessons of the project.
Three months later
The most satisfying part of Stonenine is not any individual technical feature.
It is the fact that it exists.
For years, this was just an idea I kept returning to and never built.
Shipaton created a deadline and a reason to finally commit to it. My girlfriend and I spent roughly three months turning that old concept into a real game with its own mechanics, progression, economy, animations, testing infrastructure, cross-platform architecture, security systems, and RevenueCat-powered purchases.
Somewhere along the way, the project stopped feeling like a prototype.
It became a game we genuinely wanted to ship.
And that is probably the biggest thing I learned from Shipaton:
the difference between an idea you've had for years and a product people can play is eventually deciding to build it.
Log in or sign up for Devpost to join the conversation.