the drawing probe measures near the source, not at the floor

It probed at 96 pixels and got the answer wrong on exactly the file it was written for. Downscaling a
logo to 96 resamples its flat areas into gradients, so by the time the probe sees it, it already looks
like a photograph. Lossy wins that comparison, and the verdict is applied to every rung above — where
the flat areas are still flat and lossless would have won by a wide margin.

fofos-logo-saffron came out with a 13,074-byte 160-pixel rung and a 22,432-byte 256-pixel one, beside
a 9,144-byte 300-pixel original. Three files larger than the thing they were made from.

480 now, or the source's own width when it is smaller — which is the case that matters, because that
is what a logo is. Not the full size: a picture is still recognisably itself at 480, and probing a
4000x6000 photograph losslessly to learn something already obvious is minutes of encoding across a
library.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 09:54:17 +01:00
co-authored by Claude Opus 5
parent 5481005ccf
commit 193f8cb03d
+26 -3
View File
@@ -164,6 +164,9 @@ const THUMB_MAX_WIDTH = 640;
*/
const SQUARE_TOLERANCE = 0.02;
/** Where the drawing-or-photograph question is asked. See prefersLossless for why not smaller. */
const PROBE_WIDTH = 480;
/**
* Is this a DRAWING rather than a photograph?
*
@@ -191,8 +194,28 @@ const SQUARE_TOLERANCE = 0.02;
*
* Ties and failures go to lossy, which is the right default for a library that is mostly photographs.
*/
async function prefersLossless(sourcePath: string, staging: string, email: string): Promise<boolean> {
const probe = LADDER[0]!;
async function prefersLossless(
sourcePath: string,
staging: string,
email: string,
sourceWidth: number,
): Promise<boolean> {
/*
Measured near the source's own size, NOT at the smallest rung.
The first attempt probed at 96 pixels and got the answer wrong on exactly the file it was written
for. Downscaling a logo to 96 resamples its flat areas into gradients, so by the time the probe
sees it, it already looks like a photograph — lossy wins the comparison, and that verdict is then
applied to every rung above, where the flat areas are still flat and lossless would have won by a
wide margin. fofos-logo-saffron came out with a 13,074-byte 160-pixel rung beside a 9,144-byte
300-pixel original.
480 rather than the full size: a picture is still recognisably itself at 480 — flat stays flat and
photographic stays photographic — and probing a 4000x6000 photograph losslessly to learn something
already obvious is minutes of encoding across a library. Anything smaller than 480 is probed at its
own size, which is the case that matters, because that is what a logo is.
*/
const probe = Math.min(PROBE_WIDTH, sourceWidth);
const lossyPath = join(staging, '.probe-lossy.webp');
const losslessPath = join(staging, '.probe-lossless.webp');
@@ -554,7 +577,7 @@ async function buildImage(
photograph want opposite encoders, and getting it wrong on a logo produced a smaller rung two and
a half times the size of the original.
*/
const lossless = await prefersLossless(master, staging, email);
const lossless = await prefersLossless(master, staging, email, width);
const variants: Variant[] = [];