Murder Plot
A deduction game for Android made 100% in Jetpack Compose, with no game engine. I designed and built it on my own, from the case generator to the sound effects, and it's now live on Google Play.
- Role
- Everything: design, code, art, sound and publishing
- Technology
- Kotlin and Jetpack Compose, no game engine
- Status
- On Google Play since October 30, 2026
- Languages
- Spanish and English
What it is
Murder Plot is a murder sudoku: you place each suspect on the floor plan by following the clues, no two share a row or a column, and the rule of the case gives the culprit away. It uses no game engine: the board, the scenes, the animations and the sound come from the same tools I use in any Android app.
Over 35,000 lines of Kotlin across the app and its puzzle module, and 141 automated tests.
How it plays

The case
The victim, the suspects and the rule that will give the culprit away.

The floor plan
You tap to place each suspect, and clues check themselves off as they are met.

The reveal
Coins and up to three stars: solve it, use no help and beat the par time.
No game engine
What an engine usually handles is handled here by Compose and Kotlin.
Rendering
The casebook scenes are pure Canvas: about 8,000 lines of immediate-mode drawing. The static part is recorded once, darkness is cut out with blend modes, and there are no sprites or shaders, so it runs from Android 8 up.
Physics
Curtains and towels are Verlet cloth, lamps swing like pendulums and candle flames move with noise. Your finger can push the curtains or leave ripples in the water.
Time and animation
A withFrameNanos frame clock drives the scenes. Time is read only while drawing, so nothing recomposes on every frame. The rest is Compose animation: transitions, springs and animated state.
Game state
A ViewModel keeps the immutable game state in a StateFlow. Every move produces a new state, up to 50 steps can be undone, and the game saves itself.
Sound
27 effects synthesized with my own Python script, with no third-party samples. They play through SoundPool and shift pitch slightly so they never sound repeated.
Saving
DataStore on the phone and Play Games Saved Games in the cloud. A copy with fewer solved cases never replaces a more advanced one.
Five settings drawn in code
Each casebook is an illustrated path that reveals itself as you solve its cases.
Cases with exactly one solution
Cases come from a pure Kotlin module with no Android in it. It generates the 130 cases of the casebooks and their seasons, plus the 365 daily ones, and it also runs on the phone to create endless free cases.
Six candidates are generated for each case, and the one that matches the target difficulty wins.
- RoomsThey grow at random until they fill the floor plan.
- SuspectsOne per row and per column, and the rule of the case points to exactly one.
- CluesTrue facts picked at random while they help deduction, so they combine with each other.
- CleanupClues that are not needed are removed.
- VerificationA solver requires exactly one solution, reachable by deduction alone.
Decisions
Ads that respect the game
An ad never interrupts a case. Rewarded ads are the player’s choice, to save a streak, earn an extra magnifier or double the coins. The only one that shows up on its own appears after a reveal, at most once every two cases, never in the first days and never with the pass.
You learn by playing
The first game is a handwritten 4×4 case played on the real board, step by step, instead of an instructions screen. It can be replayed from Settings.
Never unlimited hints
A button that solves the case empties the game. The magnifier points out who is misplaced, or tells you who you can deduce next and with which clue, and it has a limit per case.
You pay for a new world
The pass removes ads and unlocks the Imperial Theater, a weekly 7×7 case and more hard cases. It gives no edge: it earns no extra points on the weekly leaderboard.
The same case for everyone
The game day changes at midnight UTC-7, like the Play Games daily leaderboards, so everyone plays the same daily case at the same time. There are 365 daily cases: easy on Mondays, 7×7 on Sundays.
Spanish is the key
Every text is written in Spanish, and that sentence is the key to its translation. Clues are stored as facts, not sentences, and rewritten in English on load. A test fails if a translation is missing or left over.
Play Games is optional
The game asks for no profile when you open it, and everything works the same without signing in. If you connect, progress is backed up to the cloud, and a copy never replaces one with more solved cases.
What it is built with
- Game
- Kotlin, Jetpack Compose, Canvas, Navigation Compose, ViewModel and StateFlow
- Data
- DataStore, kotlinx.serialization, WorkManager for the daily reminder and Firebase Analytics with consent
- Google Play
- AdMob with UMP, Play Billing for the pass and gems, and Play Games: achievements, a weekly leaderboard and saved games
- Tooling
- A pure Kotlin puzzle module, 83 tests, sound synthesized in Python and store screenshots automated over adb
Contact
To talk about a project, an opportunity or something you saw here.




