Inspiration
Every focus app asks you to be disciplined at exactly the moment you have no discipline left. They give you a countdown and a guilt trip. Neither helps a hand that wants to reach for a phone.
What it does
Squishflow puts a soft body between you and the work.
You tell it what you want to finish. It hands back blocks short enough to believe — the first one deliberately shorter, because starting is the expensive part of focus, not continuing. Then, while you sit with the tension, you push your thumb into Squishy and it pushes back.
The squishy is a physics model, not an animation. The silhouette is a ring of 26 coupled radial samples with real stiffness, damping and neighbour coupling. A press opens a dent exactly under your finger, the wave travels around the surface, and volume conservation means denting one side bulges the other. It has haptics tied to pressure and a synthesised voice: no audio files ship, and every material's squelch is derived from the same three numbers that drive its shape — stiffness sets the pitch, damping the length, coupling the wetness — so a stress ball snaps high and dry and a bubble rings. Left alone, it wakes up, blinks on an irregular rhythm, looks around, and occasionally stretches. Squeeze it and it answers with a word for a beat (Oof. — mid-block only Mm., Back to it.); press hard and the mouth purses; finish a block and the eyes close for a moment while the smile widens. Each of the five materials wears its own face: smaller eyes on foam, heavy lids on mochi, a startled bubble.
It does the thing it asks of you. Over a focus block the body softens and stops wobbling. The same drag gives about a fifth further at minute twenty-four than at minute one. Then, in the last sixty seconds, it breaks the pattern: the breath quickens and the line changes to Almost banked. A session visibly has depth, not just a countdown.
It has a position on streaks. Come back after three days away and it says Been a while. Nothing to catch up on. That is the moment a streak app shows a broken chain; this one says the gap does not count — and that sentence, like every line the app says, is held to rules by unit tests: no exclamation marks, no failure language, nothing longer than one line. The journey screen shows the last seven days as minutes per day, scaled to your own best day, because minutes done cannot break and days missed are nobody's business. When a block ends with the phone face down, one notification says so — "Banked. 15 min of focus." — and that is the only notification the app will ever send.
Monetisation: focus is the currency
There are five bodies. Every one can be earned with focused minutes. Pro buys them now.
A focus app that locks its rewards behind a card is working against the thing it claims to want — it profits when you pay, not when you concentrate. Making focused minutes the free currency means the app only gets more rewarding the more it actually works. The subscription is a shortcut for people who would rather not wait, and the reason there are no ads and no analytics on anyone, paying or not. The one Pro benefit that is not cosmetic is utility: free protects three apps behind Squishy, Pro protects every one — and that limit lives in product logic with a test, not in paywall copy.
Two consequences, both unit-tested: a lapsed subscription keeps everything you earned, and a locked body shows how close you are rather than a padlock. And because only one body is free at the start, tapping a locked one hands it to you for six seconds before the paywall appears. A list of names cannot communicate what dense foam feels like.
The materials are not reskins. A test asserts that a stress ball settles in fewer frames than jelly and a water balloon in more, and another that it sounds higher and shorter. If two of them ever came to rest at the same rate, the entitlement would be selling paint. The shelf plays each body's voice when you tap it, locked or not: the difference is heard before it is paid for.
The plans screen is drawn in the app's own style over RevenueCat's SDK: three plain rows with the yearly price explained per month, a store trial said in plain words when one exists, Restore purchases, privacy and terms beside the button, and no countdown or "most popular" badge. A purchase unlocks on the spot. The release build is minified, signed from the environment and refuses the Test Store key, so the distance from here to charging real money is two store accounts and two keys.
How we built it
Kotlin Multiplatform and Compose Multiplatform. The UI, timer, planner, physics and audio synthesis are all in commonMain; the platform source sets hold only what genuinely differs — haptics, persistence, app selection. RevenueCat's KMP SDK handles offerings, purchase, restore and the Pro entitlement; the plans screen itself is drawn in the app's own style.
It runs on Android and iPhone from one codebase. The iOS build lives in GitHub Actions because the only Mac available runs Monterey: a hosted Mac compiles the shared Kotlin for Apple, builds the Xcode project, boots a simulator and photographs the app on every push. The shared Kotlin compiled for iOS on the first attempt.
A few design decisions worth knowing:
- Hue carries state. Coral is tension, sage is focus, lavender is interruption — across the whole product. Materials therefore never repaint the body; they differ by finish.
- Motion has a grammar. Going deeper rises, going back falls, overlays travel their own axis, and an interruption arrives with no motion at all, because being pulled out of a distraction should feel like a stop.
- The first screen is the product. One body and nothing to read before you touch it. The list of apps and the one permission come after you have squeezed it, not before.
- Reduced motion is honoured. The body still deforms under a finger — that is the interaction — but nothing moves that you did not move.
- The copy is tested. No exclamation marks, no failure or streak language, nothing longer than one line. The rules are assertions.
Honesty: the planner is deterministic and on-device. It does not learn and there is no model behind it. The interface says "adaptive focus," never "AI."
Challenges we ran into
The project is developed entirely on Windows. The only Mac available for iOS work runs Monterey and cannot target a current iPhone or run a current Xcode, so the whole iOS path — compiling the shared Kotlin for Apple, building the Xcode project, running it in a simulator, and archiving an unsigned IPA for sideloading — was built to run in GitHub Actions on a hosted Mac instead. Getting Kotlin/Native to link against RevenueCat's Swift UI framework outside of Xcode, and getting a synthesised audio engine to sound "squishy" rather than like filtered noise, were the two places that took the most iteration.
Accomplishments that we're proud of
That the physics model is real rather than decorative — a test asserts a stress ball settles in fewer frames than jelly and that a water balloon sounds lower and longer, so the materials could not fake being different. And that the monetisation model survived contact with its own tests: a lapsed subscription keeps everything earned, and the plans screen refuses to run on the Test Store key outside of debug builds, so what a judge tries is the same code path a real purchase would take.
What we learned
That copy can be held to the same discipline as code. Every line the app says is checked by a unit test — no exclamation marks, no streak or failure language, one line only — which turned "does this feel calm" from a vibe into something that fails a build.
What's next
The store release path (docs/MONETIZATION_LAUNCH.md) is ready — signed release build, real RevenueCat keys, App Store and Play Store products — pending the store accounts.
For judges: testing Pro
The build in the repository uses RevenueCat's public Test Store key, so the plans screen loads real offerings and every purchase is simulated and succeeds: tap any plan, confirm the Test Store dialog, and every body unlocks on the spot. "Restore purchases" works the same way. No promo code is needed. On a store build the key changes and nothing else does.
Log in or sign up for Devpost to join the conversation.