Projects

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

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.

  1. RoomsThey grow at random until they fill the floor plan.
  2. SuspectsOne per row and per column, and the rule of the case points to exactly one.
  3. CluesTrue facts picked at random while they help deduction, so they combine with each other.
  4. CleanupClues that are not needed are removed.
  5. VerificationA solver requires exactly one solution, reachable by deduction alone.

Decisions

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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

See the other projects

Contact

To talk about a project, an opportunity or something you saw here.