IRO UTSUSHI

An app that never asks you to name a feeling. Drop color onto water, stir it with your finger, and the movement of your hand stays behind as a single picture.

Available on the App Store
  • App
  • iPhone / iPad
  • Swift
  • SwiftUI
  • Metal
Get it on the App Store

What you can do

How it is built

App Swift + SwiftUI, written as a Swift Playgrounds App Package, with zero external package dependencies.
Water A fluid simulation written in Metal. The velocity field, pressure projection, advection, vorticity confinement, and pigment mixing all run on the GPU.
Pigment Held as Lagrangian particles and layered with subtractive mixing following the Beer–Lambert law. Because they are particles, they do not blur away through diffusion over time.
Look Refraction and speculars drawn from normals derived analytically from the height field of the water, plus a grain noise and dithering to avoid banding.
Input Taken from raw UIKit events, passing every coalesced touch within a frame to the fluid. Up to five simultaneous touches are recorded, replayed, and measured as independent strokes, one per finger.
Storage The history list holds only lightweight information in JSON; the image is a single PNG; the touch sequence is fixed-width binary compressed with LZFSE, in a directory of its own per recording.
Replay and export The water is rebuilt by rerunning the simulation from the stored steps. MP4 is not a screen recording: frames are recomposed from those steps and written out at 30 fps.
Sound Synthesized inside the app, a pentatonic scale with up to 16 voices. There are no audio files.
Self-checks Since the environment cannot host XCTest, a set of checks written for this app (SelfChecks.swift) runs on debug launch instead.
Encryption None at all; the export compliance answer ships with the app.

The interesting parts

The tail keeps curling after your finger has passed

Advection carries a MacCormack correction, which preserves the thin vortices that plain semi-Lagrangian advection erases. A finger does not push the water: it drops a counter-rotating vortex pair along the direction of the drag, so the water keeps curling for a while after the finger has gone. Every damping and follow coefficient is expressed per second, so the motion is the same on a 60 Hz device and a 120 Hz one.

Separating the wave you see from the wave that moves things

The visible strength of a ripple and its effect on pigment are separate parameters. And a ripple moves the position of the pigment directly, not its velocity. Pigment velocity is pulled back towards the velocity of the water every frame — with a time constant of roughly 0.36 seconds in dense areas — and a ripple lives for less time than that, so measurement showed that adding velocity barely moved the pigment at all. Tuning the look and tuning the feel no longer break each other.

Deciding not to make pigment attract pigment

There used to be a cohesion force pulling pigment together, but the only place it could settle was a round blob: given enough time the water filled up with opaque circles that never came apart again. The edge darkening of watercolor is now reproduced without moving any pigment. The density field stays as it is, and the edge is drawn from it as shading on the water surface path, so nothing about the picture sets solid.

The same recording always replays as the same picture

The simulation is fixed at 60 steps per second, and each recording stores the actual step numbers at which UIKit input was consumed, the number of lead-in steps, and a sidecar of Float32 timestamps with UInt16 coordinates. Replay trusts those stored steps over the wall-clock timestamp. Speed is measured against UITouch.timestamp, because movements within a single frame cannot be told apart by the wall clock. Predicted touches are used only for estimating speed; they never affect where pigment lands.

On top of that, SimulationEngineRegistry routes each stored simulationVersion explicitly to the engine that matches it. Version 6 is frozen down to its constants, its clock, and its ink transfer rate, and an unknown future version is never quietly fed to the newest simulation. The point is that the picture of an old recording is not rebuilt into something else by an app update.

Not showing the score first

The four measures are not clipped at a ceiling; they sit on a curve that approaches 100 asymptotically, so one quick movement does not earn full marks. Even then, they are hidden by default on the result screen. If an app that refuses to sort feelings into words leads with numbers, those numbers simply become the new label.

Privacy and contact

IRO UTSUSHI stores your recordings on your device only. It has no account registration, no advertising, no analytics, and no features that track users. For details, see the IRO UTSUSHI Privacy Policy.

For feedback and bug reports, you can also reach us through the Sizu dev contact page.