NIJIMI

The engine

How the paper got wet.

The part of Nijimi that took the most work was the sheet itself. This is that work in the order it happened, including the parts that did not work.

An early sheet: textured cream paper, a pale yellow wash, and gold and black brush lines. A recent sheet: white paper, with red, yellow and blue meeting and mixing where they touch.
Two sheets saved by the app. Left, the first engine: a paper texture, and inks that sat beside each other. Right, the engine that shipped: white paper, and three colours mixing where they met.

What a painter knows first

Before any of the engine was judged, the medium was written down from the painter's side: not how watercolour looks, but how it is handled. Each of these became a test the app had to pass.

In the order it happened

01

Version 1

Ink that blurred

The first sheet spread ink by blurring it outward a little every frame. It looked soft, and it was wrong in ways that were not obvious at first: edges were fuzzy where real ink is crisp, drops dried into hollow rings, and long strokes left dashed tails. A blur spreads evenly in every direction. Water does not.

Replaced.

02

Version 2

Paper that drinks

The second engine treats the paper as a material. Every spot on the sheet holds some water, and water moves from wetter spots into drier ones through the fibres, the way it wicks along real paper. Ink only goes where the water carries it. That one change gave hard edges on dry paper and soft ones on wet, without asking for either.

Its first run drew nothing at all. The graphics surfaces it painted into could not be shown on screen, and it took switching them to the processor to see a mark. Then it ran at under three frames a second, until the paper texture was drawn once instead of every frame, which brought it to sixteen.

Kept. Everything since is built on it.

03

Version 3

Six numbers, found with a brush

How far water travels, how fast it dries, how wide a stroke lands and how dark its edge sits are all numbers, and no amount of reasoning finds the right ones. So a panel of sliders went into the app, and the values were found by painting, on the phone, against reference paintings. The panel came out again once they were baked in.

blend 0.75, distance 2.78, speed 2.87, width 3.78, dry 0.09, edge 0.25

Kept, and the panel went back in twice more when something changed underneath it.

04

Version 4

The dark edge, chased and given up

When a wash dries, pigment is carried to its edge and leaves a darker rim. It is one of the things that makes watercolour look like watercolour, and it took attempt after attempt to fake it: moving pigment toward drier paper, depositing it where the water stopped, pinning the edge so it could not creep. Each got closer and each still read as a soft blur.

Writing down why led to the real answer. The rim is made by water flowing outward, and this engine has no flow, only water evening itself out. A new engine with real flow was designed in full. It was never built, because the phone's graphics surfaces hold eight bits per colour, and every textbook way of simulating flow falls apart at that precision.

The rim work was taken back out, and the sheet got its ink back. The design is kept for later.

05

Colour

Blue and yellow made grey

Screens mix light, and paint mixes the opposite way. Add blue light to yellow light and you move toward white or grey. Mix blue paint into yellow and each pigment absorbs what the other reflects, and green is what is left. The app was mixing like a screen.

The best known fix, Mixbox, is licensed for non-commercial use only, so it was ruled out. The answer was spectral.js, an openly licensed library that works out a mix across 38 bands of the light spectrum. That is far too much to do for every spot on the sheet every frame, so it is done once, on a computer, ahead of time. The results are stored as small tables of colour, and the app only looks the answer up.

Six squares. Each shows two pigments mixed at every strength and ratio: red into black, yellow into blue passing through green, sienna into green, green into violet, green into crimson, orange into teal.
Six of the tables. Along one side, how much of the second pigment; along the other, how much pigment there is at all. Yellow into blue passes through green, as paint does.
Eight horizontal colour ramps, each running from one pigment to another through their real mixtures.
The same idea as ramps, from one pigment to its partner. Blue to yellow goes through green, not grey.

Kept. The palettes built on it.

06

Colour

Any colours, not pairs

The tables had a limit built in: each spot on the sheet could only hold two pigments, so the palettes were locked pairs, a dark and an accent. Real palettes are not pairs. So the sheet was changed to record how much each spot absorbs of red, green and blue light separately. Any number of colours can now meet, and mixing stays subtractive, because light either colour absorbs is gone.

It costs a little accuracy. Three channels cannot tell apart two colours that look the same but are made differently. Mixes are right in direction and a little off in how vivid they are. Being able to use a real 24 colour tin was worth it. Later, washes layered over dry ones were changed to darken through the same pigment model, so a glaze reads as a glaze.

Kept. What five colours from the tin can reach.

07

Interface

A dish, a dial, then stacks

With real mixing working, the obvious next thing was a place to mix: a dish you drop colours into and stir. It ran the real engine and it worked, and it lost, because on a phone it took space from the painting for something the paper already does. Then a radial dial of the whole tin, then a single arc of colour from warm to cold. Both lost to five colours at a time on a card you swipe.

The dish, the dial and the arc are kept as pages. The stacks are what shipped.

08

Speed

From 144 milliseconds a frame to one

Measured on an iPhone 17 Pro while painting, the sheet took 144 milliseconds a frame: seven frames a second, which feels like dragging a finger through mud. Cutting it back to one simulation step under load brought it to 80. Almost all of that was the processor running the water twice over 148 thousand spots on the sheet.

At first 144 ms One step under load 80 ms On the graphics chip 1 ms, 118 frames a second

Then the whole loop was moved onto the phone's graphics chip, which is built for exactly this kind of work. The same simulation took one millisecond. The first version of that crashed after a few seconds, because every image the loop made had to be freed by hand.

The speed exposed something. Every number found with the brush in version 3 was found at seven frames a second, when the sheet was only living through a third of a second for every second on the clock. At 118 frames the paper dried three times sooner in the hand and every stroke came out scratchy. So the sheet's clock is held at the pace the tuning was made at, and the speed went into simulating the sheet at the full size it is shown, where before it ran at two thirds.

Kept. The numbers are below.

09

Undo

Built, and taken out

Undo went in, eight strokes deep, and took six builds to stop crashing and still worked only some of the time. A wet stroke mixes into what is already on the paper, so it cannot be subtracted back out. The only way to undo it is to keep whole copies of the sheet, and copies of a sheet that is still moving kept going stale. It came out, and the button that starts a new sheet came back.

Removed on purpose. Why there is no undo, and the page that argued for it.

10

Water

Rounding, found in the simulator

On the phone, water left a grey mark that never dried, colour stopped spreading within seconds, and tilting took a hard lean. All three came from the speed-up. The sheet stores every value in 256 steps, and at 118 frames a second the change in one frame was smaller than a step, so it rounded back to nothing. Water never left the paper. Gravity had also been multiplied by the length of a frame, so it was a seventeenth as strong as when it was tuned.

The sheet now moves on thirty times a second whatever the screen does, the water is nudged by a random fraction of a step before it is stored so that small changes add up, and gravity is a fixed push. The same tests showed that the sheet still resolved colour from the two inks of the card in hand: choosing a colour from another card froze the painting into a flat picture, and new paint could only lie over it. Now every spot carries the colour of its own paint, so any colours that meet while wet mix.

Kept. Before and after, painted by the app itself.

11

Colour

Fewer paints, in more than one strength

The stacks had grown to as many as 24 paints, repeated colours from one card to the next, and every swatch painted at full strength, so marks landed dense. The first palette the app ever had was four inks in three strengths each. The palettes went back to that: four of them, each a few paints, with washes and pale washes on the card beside the full colour. Choosing a palette moved to its own screen, opened from a button beside home.

Kept. The whole search for a palette, and the proposals that ended it.

12

Edges

Crisp on dry paper, soft on wet

Held against real watercolours, every stroke came out blurred, like an airbrush, whether the paper under it was wet or dry. Three things did it. A stroke was a row of overlapping stamps, each adding paint, so the middle piled up and the edges faded. Dry paper took water as readily as wet paper, so every mark bled. And each step read its neighbours between pixels, which softened the whole sheet a little, thirty times a second.

A stroke now lays one even film. Dry paper takes almost no water, so a mark on it keeps its edge, and pigment spreads only through water that is standing on the paper, as far as both sides are wet. The sheet reads whole pixels. A wash is wet for about fifteen seconds and dry in about thirty, and colour laid over a dried wash glazes instead of mixing in.

Kept.

Not yet

Still open

What watercolour does that the app does not

The dark rim of a drying wash, from version 4. The ragged cauliflower bloom, where water dropped into half dry paint pushes it outward. Salt, which leaves pale stars. Splatter from a flicked brush. Drips that run with a tail. Blowing paint across the sheet with a straw. Most of these need water that flows rather than water that evens out, which is the engine that was designed and not built.

The measurements

Painting continuously on an iPhone 17 Pro.

Where the loop ran Typical frame Worst frame Frames a second
Processor, two steps a frame144 ms156 ms7
Processor, one step under load80 ms90 ms12
Graphics chip1 ms2 ms118

Every investigation behind the interface and the colour