One Sentence in the House Sign
Every family repository has a CLAUDE.md that says what can't be reconstructed from the code. Ariandel's design philosophy opens with this line:
"Brush physics is libmypaint's, vendored untouched. Everything an app should own — tiles, layers, compositing, persistence, input, interface — is ours."
That sentence draws a border. Getting the border right matters more than anything you do on either side of it, because it decides which problems you get to solve once and which ones you'll solve forever.
What the Engine Gives You
Three things, and each one is the hard kind.
- Dab physics. Where each dab lands along a stroke, how it's rasterized, how smudge picks up color, how pigment mixes. Architecture invariant 1 names all four as libmypaint's.
- A brush model. 65 settings, each one a base value plus curves over 18 inputs, stored as
.mybpresets. Ariandel loads them through libmypaint's own parser (mypaint_brush_from_string), so MyPaint's brush format and its CC0 brush library come along for free. That's also why json-c rides along: the parser needs it. - A tiled-surface contract. libmypaint doesn't own pixels. It asks a
MyPaintTiledSurfacefor 64×64 tiles of premultiplied 15-bit RGBA, paints into them, and hands them back. Its header says it plainly: "The size of the surface is infinite, and consumers need just implement two vfuncs."
What You Have to Own
Look at what's left once the engine has done its part. The evaluation's table says libmypaint has no layers, "by design: the surface interface is the host's job." That's the engine telling you where your work starts.
| Part | Where it lives in Ariandel |
|---|---|
| Tiles | Ariandel's own sparse tile store behind that two-function contract; the store is also where undo captures each tile's before-state |
| Layers | Layer.swift and friends: opacity, visibility, blend modes, groups, masks |
| Undo | History.swift: tile-exact steps, bounded by bytes |
| Files | ArtworkFile.swift: the .ariandel package, every layer's tiles in the engine's native format, LZFSE-compressed |
| Compositing | The Metal compositor in CanvasRenderer.swift |
| Input | PencilMapping: the one place Pencil force, altitude, azimuth and roll become libmypaint inputs |
| Interface | The iPad app itself |
| Companion | Not built here at all: cwkPippa's Sidekick panel, embedded beside the canvas |
The seam between the two halves is a single call. Every sample from the Pencil enters libmypaint through mypaint_brush_stroke_to, carrying position, pressure, both tilts, elapsed time, view zoom, view rotation and barrel rotation. The brush queues its dabs on the surface; the surface owns where they land. Nothing in Ariandel's Swift reaches into libmypaint's internals, and nothing in libmypaint knows a layer exists.
A Sketchbook Is a Start, Never a Reduction
There's a classic way to shrink an ambitious project without anybody noticing: ship the smallest thing that technically works and quietly let it become the plan. For a painting app, that's the single-layer sketchbook. The house sign closes that door by name: "The ambition is Procreate-class. A single-layer sketchbook is a milestone, never the product." And it records Dad's ruling from the first day: "Basic layers are not hard." Layer handling is familiar implementation work. The genuinely hard, genuinely unknown parts are brush feel, Pencil responsiveness, and making it all hold together, and those are exactly what the engine choice was protecting.
The git log shows how seriously that was meant. The repository was born at 21:51. The engine package landed at 22:09. Layers, blend modes, history and the .ariandel package landed at 22:14. The Metal compositor at 22:19. Build 1 went to TestFlight at 22:51, and it already had layers. The single-layer sketchbook never shipped, not even once.
Where the Growth Went
At build 24 the numbers tell the same story. libmypaint's 5,419 lines of C sit in a directory that exactly one commit, of 167, has ever touched: the one that put them there. Ariandel's own C bridge has been touched by 22 commits and has grown to about 2,800 lines. The engine package holds about 14,400 lines of Swift, the app about 17,500. All the growth landed on the side Ariandel owns. That's what a healthy border looks like from the inside.
The Name
Ariandel takes its name from the Painted World of Ariandel in Dark Souls III, a world a painter paints anew. It fits the boundary rather well. Dad is the painter. Pippa lives in the world, looks at it and talks about it, and never paints it for him.