How I build Garmin watch faces with AI agents

·

21 min read Original article ↗

I make Garmin watch faces. There is one engine, and a family of faces sitting on top of it. The engine and the first face I typed out myself, line by line, over about a year. Since last July I have been building with coding agents instead. This is what that summer looked like: what the agents made possible, what they cost, and the rules I had to write to make them work.

A row of Garmin watch faces from the family, side by side, each with a different dial
The family, side by side. One engine underneath every one of them.

That year, August 2024 to the summer of 2025, was about two hundred commits. It produced Three Lights, on the Connect IQ store since March 2025, and the two ideas every later face is built on: a drawable contract, and the partial clip handling that lets a watch repaint one small rectangle instead of the whole screen.

The second half starts on 6 July 2026, with Claude Code: roughly twelve hundred commits in three months. The engine grew, three of the old faces were rebuilt on top of it, and a shelf of new ones got made. It cost one month of Claude Pro and then two months of Claude Max 5x, and it paid for itself. Between 18 July and 3 September 2026 the paid faces sold 560 copies, and the two free ones each went past a thousand installs on the store.

The ports are the part I would never have done otherwise. My Day 24, Half Time and Activity Graph were written years ago for the old memory-in-pixel screens. Rebuilding them for AMOLED, on the current system, on a shared engine, across everything Garmin sells today, is months of careful and profoundly dull work. The agents made it possible, and those three are what the old buyers came back for.

What the agents do is the boring work and the research: the arithmetic, the cutting of art into layers, the measurements, the long rounds of adjust and measure again, and the writing down of everything I know about how a movement goes together. The concepts are mine, the taste calls are mine, and there is a long review at the end where I look at pictures and say what has to be reworked. That is a written rule, because an agent cannot show a picture to anybody: every visible change reaches me as an image before anyone calls it done.

We did try the other thing. Two of the faces, Auralis and Mandalis, came out of asking the agents for ideas and then building the ones I liked. Both are on the store, both are put together properly, and both sell least by a distance.

Auralis Garmin watch face, bands of green and blue moving under the time

The faces that sell are the ones somebody already wanted before they went looking: a 24-hour dial, a giant analog, a real movement. Neither an aurora nor a mandala is a thing anybody types into a store search. It settled what to point the agents at, which is everything except the concept.

Mandalis Garmin watch face, a mandala of green and blue petals around the hours and minutes

The interesting part of all this is not the model. It is the workshop the model works in: a rules document at the root of the repository, a folder of skills, a couple of standing agent definitions, and a shell script that runs whenever an agent stops. Almost every line in them is there because something went wrong once.

Every phase of the work is a folder of rules

A skill, in this repository, is just a folder: an index file, a few topic files holding the rules that actually bite, and sometimes a library of code underneath.

The one worth showing is the mechanical-dial skill. It is full of rules a watchmaker would find obvious and a programmer would never guess. A bridge is a beam, so it wants an anchor at each end. A jewel goes where an arbor turns and nowhere else. A screw that holds nothing gets deleted. The mainplate starts as a full disc reaching the caseband and you cut the openings out of it, because if you build it the other way round, as a ring with a few arms, every bridge foot outside that ring is screwed to thin air. I wrote that whole process up phase by phase in another piece.

Skeleton Garmin watch face, an openworked movement with a flying tourbillon at six o'clock

Under the rules sits a small library: the renderer, the gearing arithmetic, the placer that works out where a fixing is allowed to stand, a check that asks whether any two parts are touching, and a script that shakes every constant to see what breaks. A new mechanical face writes one file of its own, declaring what it renders and which parts the placer has to work around, and inherits the rest. The library is an engine, and what a face knows about itself is data.

On 6 September a session reworked two mechanical faces without ever opening that folder. It missed two of its rules. So the index now opens with a line in bold saying that a skill marked required is loaded before the work starts.

The mechanical-dial skill folder drawn as a file tree: an index, four topic files and a code library
One skill, as it sits on disk. An index, the topic files that hold the rules, and the library a new mechanical face inherits by writing a single file of its own.

From a drawing to something you can install

Four folders carry a face from an idea to something you can put on a watch, and they run in order. From the first concept commit to the day the store approved it, a face has taken between two days and twenty: Avionis two, Stardome three, Daedal four, Regulator twenty. The mechanical ones are always the slow ones, and the commit counts run the same way, 47 on Daedal and 218 on Regulator.

The first folder is concepts. Everything gets prototyped in Python and SVG, because that loop is seconds long and the loop inside a Connect IQ project is not. Two rules govern that phase, both learned by getting it wrong. Snap the mockup to the panel's own colour lattice straight away, because a full colour concept hides the banding the wearer is going to see. And judge the dial small, and in bad light. A face that looks wonderful at 454 pixels on a bright monitor has not been judged.

The second cuts the finished render into what the watch actually draws: an opaque background, the sprites that turn, a foreground painted on top each frame, and the hands. One rule decides that split. Nothing that turns may be baked into the background, because a baked wheel is frozen forever and even the slow ones go round once an hour. Render the frame at two different times with the part at its true angle for each, then count the pixels that differ. More than ten by ten pixels and the part turns. It got broken twice in one day in September, on two different faces, both times to save graphics memory, by an agent optimising something it could not see. On one of those faces all four wheels that had been written off as invisible failed the test comfortably.

The third builds the night frame, the dim one you get with your wrist down. It is put together from the same parts as the lit dial rather than flattened into a picture, so the train can still turn overnight and a colourway can still recolour it, and the mainplate is left out completely, because under a dark room exposure a plate that size reads as black anyway. Then it goes on the simulator's screen heat map.

Skeleton Bridge at night, the movement dimmed to a line drawing on black

On 31 August a night frame shipped on the strength of a line saying "tone curve: yes" while the code underneath was doing something cruder. The checklist was green and the checklist was wrong. So the rules say a green check is not verification, and neither is an agent telling me it passed. Verification is opening the thing itself, next to the face it was supposed to follow and the rules it was supposed to obey, and looking.

The fourth folder finishes the face: settings, data fields, and memory, where there are two absolutes. Everything a face draws is resident at once and nothing loads after layout, so a face that swaps texture sets when it goes into always-on makes its wearer wait for the dial on every single wrist raise. One of mine waited five seconds. Nothing crashed, nobody reported it, and I only know because it got measured. The memory side has its own write-up: three causes of one black dial. The second absolute: never buy memory with something the wearer can see. Take the free savings first, then measure whether the reduction actually shows.

The phases of building a watch face laid out left to right, each one a folder of rules
Each box is a folder of rules with its own scripts, and the work goes through them in order.

From a watch to a shop window

The gallery is shot in the simulator, one build per picture. The face contributes a small file listing its shots, each with a line saying why it exists, because otherwise you end up with nine pictures of the same thing.

Then the listing, in ten languages besides English. They are not translated. They are written. Translate an English draft and you carry that draft's rhythm into every file, where it reads as machine copy in Warsaw and in Lisbon. So it goes in four rounds, one agent per language per round. It starts with a fact list written as data: flat, deliberately ungrammatical, no verb where a colon will do, because anything set down there as an English sentence comes back as an English sentence in eleven files. Then a native writer for each language, handed that fact list and two listings I have already approved in the same language, and it never sees the English. Then a native proofreader. Then a reviser with web search.

On one face that last round still found real faults in every language after the other three had been through. Its main test is mechanical: take the sentence, put it back into English word for word, and if natural English comes out then it was built on English. Its other job is terms nobody actually uses, which every writer invents at once because they all reach for the same literal. Regulator's "fan gauge" failed independently in six languages. All six really say sector, or arc, or their own equivalent. Czech is the one I can hear for myself, so Czech is where I start reading.

Regulator Garmin watch face, hours on their own ring above an openworked movement

Then the upload form, tab by tab. I do the login, the file, the images and the submit. The agent fills in every title, description and what's-new, in every language tab. The whole wizard is state held in the browser, so any navigation drops it back to step one and quietly throws away the attached file and every field in it. On 25 July the agent finished twelve tabs of one listing, moved on to a job on this website, and pointed the same browser tab at localhost. All of it gone, the whole thing again from the top. So between my step one and my submit, that tab is touch-nothing territory, and it stays locked until I say I have pressed submit.

The face's page on this site is the last thing that happens, generated from the same texts.

Who does what

The session I talk to decides things, hands out the work and checks what comes back. The helper agents it starts do the building and the measuring. A long conversation fails in two ways I have watched happen. It defends its earlier decisions instead of measuring them again, and it tunes instead of diagnosing.

Every helper gets a model named for it. Roughly half the work on any face is mechanical: running the checks and reporting the numbers, regenerating sheets and exports. That goes on the cheap model, and so does the regression check, which is verification rather than judgement. The strong model keeps the design decisions, the root-cause work where a check says one thing and the picture says another, and the call about what to give up when two targets fight. On 6 September a session started five helpers and named a model on none of them.

The sentence that took me longest to arrive at: you hand an agent the thing that measures, never the number you believe. Every helper that has gone wrong on this project went wrong that way. One was handed memory figures that were themselves wrong and rebuilt a face that barely needed rebuilding. It did exactly what it was told, competently, for hours. Another was asked to confirm that a face still rendered, glanced at it, and said yes about a dial I could see was broken. So the instructions now give the command to run and the number to beat, say which measurement wins when two disagree, and tell the agent in writing that the numbers it has been handed are there to be disproved. I do not write "confirm X is fine" any more. I write "measure X and tell me the number".

The checker stopped being something I typed out each time and became a file. It carries a table of measurements with an authority column: the arithmetic over the files reads optimistic and is not the authority, the reading taken from inside the running app is.

Commit before starting the helpers, because a builder plus four checkers is five agents on one budget, and when a limit hits it takes all five at once, including the one halfway through rewriting a shared file. What survives is the last commit plus whatever is on disk, so anything that matters lives in files as it happens. And one job at a time. If I say do A, then B, that is two rounds and A comes back to me before B starts. Bundling them once turned a two-constant fix into an hour.

One session handing work out to several agents, each labelled with the model it runs on
One session decides and hands out the work, the helpers build and measure, and the commit goes in first, so a lost run costs one step instead of a day.

Words cost something too

The code I wrote by hand runs at 6.4% comments, with a median comment block of one line. By August one dial source file was at 52%. The instruction had not changed; what changed was what it meant, because "match the surrounding code" produced sparse comments in June and produced essays in August, once the surroundings had drifted.

So the limits are absolutes. Monkey C under 8% of the lines a change adds. Comment blocks of one line, two at the very outside. Python is judged by block length with the real parser instead of by percentage, because a module of short functions each carrying a one-line contract runs at forty or fifty percent prose and is exactly right. A ratio cap was tried, and it flagged those every time. A shell script enforces the lot whenever an agent stops, in half a second with no model involved, and it judges only the lines a change adds, so old code is never re-judged.

Prose has caps as well. A Markdown file over its cap may be edited or shortened, never grown: eight hundred lines for a document, four hundred for a skill document. Skills get the tighter one because a skill is read into context on every job. To add a rule to a skill, you cut a rule. The repository had no cap at all for a long time, which is why one build log that had already been split in half at 1,971 lines carried on growing until it reached 7,556.

Never give cut prose a destination. An old process file said to keep the imperative and leave the story in the study's log, so nothing was ever actually deleted, only relocated. One tidying round parked 2,308 lines in an archive while shrinking the source by 1,039. The repository grew by 1,269 lines, and the source grew back anyway. Cut it. Git has it.

Nothing lives only in an agent's memory, because memory is a private cache: I cannot read it, I cannot correct it, and the next agent does not have it. And never write into a repository file a fact that can change without a commit. Which faces are live, at what version, with how many downloads, all of that is the store's to answer, a script asks it in a second, and written down it goes stale within days. Twice in one day a stale line in the main rules file had an agent reasoning about buyers that one call would have counted.

Comment density of one dial source file, before and after it was cut back
One dial source file before and after. The instruction never changed. What changed is that the limit stopped being relative to whatever happened to be there already.

A green check is not proof

A check that cannot fail is worse than no check, because it reads as proof. So the routine is to break the thing on purpose and watch the check go red, before quoting its table as evidence of anything. The first time anybody did that here, over a table that looked mature and green, most of its rows turned out to be incapable of ever failing. Disturb the measurements as well as the thing being measured. Then say which rows genuinely pass, which are tautologies that compare a value against the thing that produced it, and which are simply unproven.

If the question is whether a render changed, decode both images and subtract the arrays. A stored hash once argued the exact opposite of the truth, and that version of the mistake reached buyers. Two watches on the same screen family, the same art, and one of them stores bitmap alpha but cannot blend it. The two frames differed by 33,229 pixels. On the second watch the steel went dark and the gold vanished, and the face had been shipping like that.

Legibility gets measured over a grid of deliberately unflattering poses. Hands overlapped, hands lying on bright metal, every indicator at both ends of its travel. Before that grid existed there was one canonical pose that happened to look good, and it overstated a hand's margin by five times. Five separate ways of damaging the hands turned nothing red on it.

Four poses of the Regulator dial, the hands at different angles over the movement
Four poses out of the Regulator grid. The flattering one is the one that lies, which is why the grid exists at all.

Three lessons about cost sit underneath all of that. The first is that tuning is not diagnosing. Several rounds once went into adjusting how the filed edge on a bridge looked, each one a small improvement on the last. A fresh agent with no history proved in a single test that the edge had never been drawn at all. Every one of those rounds had felt like progress.

The second is that nothing long runs without a price said out loud first. On 31 August one run took six silent hours, because a design tool that renders twelve poses had been wired into a per-case row, so every one of 253 cases quietly paid for twelve renders. Nobody had multiplied those two numbers together. Now, before anything long starts, items times cost per item gets said out loud, and a tool that cannot print its count, an estimate and some sign of progress does not get to run at all.

The third has my own words in it, from 20 August: "waiting 20 minutes, burning the Mac with 100% CPU, fans on the max on every small tiny change, this is really crazy." These scripts default to every core. Leave half of them free, and do not run the full set of checks after a small change. An agent will happily cook your laptop for twenty minutes to answer a question it could have answered in one second, and it will never once mention the fans.

The parts I keep

Judging the whole image, first and last. One dial had every row green and read as broken glass. Nothing measurably wrong with it, and the moment it was on the screen it was obvious. That call never goes to an agent and it never goes down to a cheaper model.

The wrist, which is small, glanced at, and usually in poor light. The Czech, which is where a listing either sounds like a person or does not, and I can hear that there long before I could see it anywhere else.

My Day 24 running on a Garmin watch on a wrist, photographed indoors
My Day 24 on my own wrist. This is the size and the light a dial has to survive, and a monitor tells you nothing about either.

The taste calls, including the one that has a name now. "Salmon pink" is what I write when a tint comes out chalky, which happens because a colourway ships as a tint over one grey master, and a tint can only ever take light away, so a deep red over bright steel comes back looking washed out. Writing "salmon pink" in a review and then committing anyway is the same failure as never having looked. A doubt you have already written down does not get to ship. Stop, fix it, or put the picture in front of somebody.

The submit button. And the emails from buyers, which are the only part of any of this that is not a file.

If you are starting this now

Write the rule down the moment you say it out loud, and put it in the repository where the next agent will read it without being asked. Hand over the thing that measures and not the number you believe. Assume any table that has never gone red is decoration. And look at the picture yourself, every time, because it is the one step nothing else in the workshop can do for you.

Almost nothing above is about which model. It is about what the model can read before it starts, what it is told to measure, who is allowed to disbelieve a number, and who looks at the result at the end. This list will look different next year, because the next batch of failures will be different.

Frequently asked questions

Do the agents write the Monkey C?

Most of it these days, and none of it unread. The engine underneath is not theirs. The drawable contract, the partial clip handling and the first faces were typed out by hand in 2024 and 2025, and everything since sits on top of that. What an agent writes gets read, and it gets written against rules that live in the repository where anyone can go and look at them, rather than in a prompt I typed once and forgot.

Does the AI design the faces?

No. The concept, the layout and every taste call are mine. Agents make the mockups I ask for, do the arithmetic, run the measurements, cut the art into the layers a watch actually draws, and grind through the mechanical work. Choosing between two options that both measure fine, and judging whether the whole thing looks right, are the parts I have never handed over.

Which tool and which model?

Claude Code, and more than one model on purpose. About half the work on a face is mechanical: running the checks and reporting the numbers, regenerating sheets and exports, re-running a measurement. That goes on the cheap model. Judgement stays on the strongest one: working out why a check disagrees with the picture, reviewing whether a movement could be assembled, deciding what to give up when two targets fight. Every helper agent gets a model named for it.

How long does a face take now?

Counting from the first concept commit to the day the store approved it: Avionis two days, Stardome three, Daedal four, Skeleton Tourbillon and Smoked Crystal five each, Colorama six, and Auralis, Mandalis and Skeleton Bridge seven each. The three ports took ten days, worked in parallel. Regulator, which is a mechanical face, took twenty. The commit counts run the same way, from 47 on Daedal to 218 on Regulator. The days go into the reviewing rather than the building.

The author builds the My Day 24 watch face family. Published 2026-09-09.