What ships today, measured; why it fails below 128px; and five concepts drawn on the macOS squircle so they can be compared at the sizes that decide the question.
I examined the shipping PNGs directly and measured them with CoreGraphics, so the numbers below are measured, not estimated. I then rasterised each of my own five directions at 1024, 64, 32 and 16 px and looked at the results, so the readings I give are observations of the drawings rather than predictions about them. That mattered: two of the five turned out to say something I had not intended, and one had a rendering defect. Both are recorded below rather than quietly fixed.
What I cannot do is judge any of this the way you will:
Every claim about the current icon is checkable — the probe commands are in the footer.
The icon is a cut-out photograph of a bristlenose pleco on a pale blue vertical gradient. It is not an illustration and not a vector mark.
Assets.xcassets/AppIcon.appiconset/, 16→1024,
all hasAlpha: no. All ten are in the built Assets.car.scripts/generate-app-icons.py from
bristlenose/theme/images/bristlenose.png — a 480×480 photo on white, background
removed by a near-white threshold, scaled to 72% of the canvas, then
LANCZOS-downsampled from the 1024 composite to every other size. There is no
per-size artwork and no hinting.33f3c8d9, "add macOS app icon with Liquid
Glass layered artwork"). Untouched in the six months since.REPO = Path("/home/user/bristlenose") — a cloud path. The icon is, as it stands,
not reproducible on this machine without editing the script.AppIcon.appiconset/layers/AppIcon.icon/ exists, with an
icon.json and four layer assets. It reaches nothing:
project.pbxproj.Assets.xcassets, where actool treats
anything that is not a valid asset type as inert.Assets.car contains exactly the ten flat PNG renditions
(icon_16x16.png … icon_512x512@2x.png) and no layered icon stack.
That is the appiconset compiling, not an Icon Composer file.Apple's documentation is explicit that a .icon file goes in the project
navigator and replaces the asset catalog icon. This one is filed where it can never
do that.
The document is also unfinished: three foreground layers are all
"hidden": false and stacked on each other; foreground-default
carries a stray hand-nudge of (16.14, −13.93) points; and the canvas declares
both an automatic-gradient fill and a background.png layer.
It looks like a session that set it up and never opened Icon Composer to check it.
Subject box and ink coverage are measured against the sampled corner background; contrast is an approximate WCAG ratio between mean subject luminance and background luminance. Anti-aliasing is what moves these numbers — as the icon shrinks, the dark fish blends into the light ground.
| Asset | Subject box | % of canvas | Ink coverage | Mean subject lum. | Approx. contrast |
|---|---|---|---|---|---|
| icon_512x512@2x (1024) | 681×355 | 67% × 35% | 11.7% | 86.2 | 5.66 |
| icon_256x256 (256) | 170×88 | 66% × 34% | 11.9% | 87.5 | 5.55 |
| icon_128x128 (128) | 85×45 | 66% × 35% | 12.1% | 90.6 | 5.29 |
| icon_32x32@2x (64) | 43×22 | 67% × 34% | 12.7% | 95.8 | 4.89 |
| icon_32x32 (32) | 22×11 | 69% × 34% | 13.4% | 102.1 | 4.44 |
| icon_16x16 (16) | 11×6 | 69% × 38% | 15.2% | 116.5 | 3.59 |
Background is rgb(212,228,244) at every size, luminance 226 — a very pale
blue. The three numbers that matter:
The concept is right and the execution is on the wrong side of the platform. A bristlenose pleco is a genuinely good mark for this product — patient, thorough, cleans a tank by grazing it. Nobody else has one. At 1024 it has real personality and it is honest about the name. None of that is the problem.
The problem is that it is a photograph, and Apple's current app-icon guidance names that specifically:
Avoid photographs. "Photos are full of details that don't work well
at small sizes or across appearances. Create graphic representations instead."
Embrace simplicity. "Fine visual features may look busy with system shadows and
highlights and lose detail at smaller sizes."
— Human Interface Guidelines, App icons
(fetched 24 Sep 2026)
The measurements are that warning happening. Specifically, where it fails:
.icon in the build, the system auto-derives all of them from one photo. Beside
apps that ship real variants, it will read as a flat sticker.One thing I want to be fair about: the .icns in the built app carries only
four entries (16, 32, 128, 256). I checked whether that was a defect and it is not — it is
normal actool fallback behaviour, and the Assets.car carries all
ten. Not a finding.
Worth confirming rather than assuming, because it changes what "good" means and because one fact removes the obvious objection.
/Applications/Xcode.app/Contents/Applications/Icon Composer.app.The app targets MACOSX_DEPLOYMENT_TARGET = 15.0
(confirmed from xcodebuild -showBuildSettings on the Release/Bristlenose scheme,
not from the pbxproj). The natural objection is that an Icon Composer icon would abandon
Sequoia. It does not: "If your app supports previous releases … Xcode automatically
generates app icon images at build time for those releases from the Icon Composer file."
One .icon covers both eras. Adopting it costs nothing in back-compatibility —
which is the fact that makes this a cheap change rather than a trade.
Distinct in concept, not in colour. All five share one palette deliberately, so you are
comparing ideas rather than hues: a Prussian-blue ground (drawn from the project's own
Edo accent, #0f5c9e) with a bone foreground. That pairing is itself taken from
the animal — dark body, cream bristles and spots — so the palette keeps the pleco even where
the form does not.
Each is drawn at 1024 and then rendered down, which is exactly what Icon Composer does from a single design. So the small cells are a fair test, and any direction that dies at 16 px here will die there.
Strokes are deliberately fat. At 16 px a stroke needs roughly 1.5 device pixels to survive, which is about 96 units in a 1024 canvas. Anything thinner is decoration that only exists in the App Store listing.
The blown-up 16 px blocks under each direction are real rasterisations at 16×16, upscaled with nearest-neighbour. That row is the whole argument. If a mark is not decided there, no amount of 1024 craft rescues it — and it is the one view that the current icon has never been designed against.
Three things only surfaced once the drawings were rendered and looked at, and all three would have been stated wrongly if I had reasoned from the vector source instead:
This is the same discipline the icon itself needs. The current photograph was never rendered at 16 px and examined — if it had been, the 39-ink-pixel smudge would have ended the approach in March.
Replace the photograph with a flat graphic mark, and author it in Icon Composer as layers.
Not because the fish is wrong — the fish is the best thing about it — but because a photo is the one decision that caps how good this can get. It cannot be layered, so it cannot take the system's glass; it cannot produce honest dark and tinted variants; and its detail is exactly what 16 px destroys. Every other complaint on this page is downstream of that single choice.
If you want a smaller change than a redesign: crop and scale the existing fish so it fills the canvas. The subject box is 67% × 35%; a tighter crop on the head and bristle fan, filling perhaps 80% × 70%, would roughly double the ink and lift small-size contrast measurably, for an afternoon's work and no new artwork. It would not fix the appearance variants or the glass, and it would still be a photo — but it is a real improvement, and it is reversible.
B, the snout. It was the strongest when rendered, it improved most under revision, it is the only one that keeps the animal the product is named after, and it is the only one whose 16 px block still carries a fixation point. A has the better silhouette and the wrong referent; C has a good silhouette attached to "ruler"; D and E I would drop.
The hybrid worth drawing that I have not: B's head with A's fan properly integrated — the fan emerging from a broad snout edge rather than converging on a point, which is both truer to the animal and the specific change that would break A's hand reading. That is one mark, not two, and it is where I would spend the next hour.
AppIcon.icon at the project root — not inside
Assets.xcassets, which is where the current one is buried.scripts/generate-app-icons.py with it.