Skip to content
Merged
41 changes: 28 additions & 13 deletions LOGO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

The logo is an export of the `/f` visualization ([src/pages/f.astro](src/pages/f.astro)):
the unit box plus the polyline that strokes an **F** — the powers
z₀, z₀², z₀⁴, z₀⁸ of the seed **z₀ = 0.362 + 0.953i** under repeated squaring
(the 3:41 moment of Numberphile's [Mandelbrot video](https://www.youtube.com/watch?v=FFftmWSzgmk)).
z₀, z₀², z₀⁴, z₀⁸ of the seed **z₀ = 0.363 + 0.965i** under repeated squaring
(a nudge off the 3:41 moment of Numberphile's [Mandelbrot video](https://www.youtube.com/watch?v=FFftmWSzgmk),
z₀ ≈ 0.362 + 0.953i — the exported mark tunes the seed for shape).

## Requirements

Expand All @@ -14,23 +15,37 @@ z₀, z₀², z₀⁴, z₀⁸ of the seed **z₀ = 0.362 + 0.953i** under repea
- **First 4 line strokes only** (z₀ → z₀¹⁶): the F itself, without the fly-off tail.
- **Without the circle** — no lock ring, no F glyph, no coordinate label, no axes.
- **Lines only — no points.** The export draws no orbit points; the page keeps its.
- **Line boldness matches the page as seen**: 5.2px strokes — 2× the page's
stroke-to-box ratio, because a PNG is typically viewed scaled down, which
visually halves its strokes next to the live canvas. Alphas box 0.4 /
polyline 0.55, at the page's reference plane scale
`min(1440/3.6, 900/2.35) ≈ 382.98 px/unit` → **766×766** canvas.
- **One stroke weight across every export**: a single stroke-to-image ratio,
matched to the **Airbnb Bélo's** line weight — the Bélo's uniform line
measures 7.023 units against its 99.9-unit symbol height in the official SVG,
so `STROKE_RATIO = 7.023/99.9 ≈ 0.0703` (**≈54px** on the 766 logo, 12.7px on
the 180px apple-touch, 4.5px on the 64px favicon; floored at 2px as a safety
net). Our box spans the image edge to edge, so the image size is the mark
height and the ratio carries over directly. The mark reads at the same
boldness whether it's the full logo or a 64px favicon.
This is heavier than the page's own strokes and intentionally does not track
them — the reference plane scale `min(1440/3.6, 900/2.35) ≈ 382.98 px/unit`
sets the **766×766** canvas geometry, but stroke weight is the export's own.
(The page itself draws strokes and points *proportional to its plane scale* —
2.6px / 4px at the reference scale — and backs its canvas at
`devicePixelRatio`, so its look is viewport-invariant and crisp.)
- **Mint ink** (`rgba(176, 222, 240)` — the same mint phosphor as the live
visualization), on `#0a0a0a` plus a transparent-background variant.
- **Single mint tone, no overlap darkening** — unlike the page, which layers a
dimmer box (α 0.4) under a brighter polyline (α 0.55), the export draws the
box and the F as **one stroked path in a single alpha (0.85)** — the
favicon/apple-touch brightness, applied to the full logo too so it doesn't
read dim. A lone `stroke()` unions its own coverage, so where the polyline
crosses the box the overlap stays that flat color instead of compositing two
translucent strokes into a denser square. Holds for the dark-bg and
transparent variants alike.
- **Mint ink** (`rgba(176, 222, 240)` at α 0.85 — the same mint phosphor as the
live visualization), on `#0a0a0a` plus a transparent-background variant.

## Favicon

The logo is also the site favicon. Same composition, but strokes and
points scale with the icon size (alphas boosted to box 0.7 / polyline
0.85 / points 1) so the mark stays legible at tab size; always on the
dark background.
The logo is also the site favicon. Same composition, single-tone treatment,
stroke ratio, and ink alpha (0.85); strokes therefore scale with the icon size
(floored at 2px) so the mark stays legible at tab size; always on the dark
background.

- [public/favicon.png](public/favicon.png) — 64×64, linked as `rel="icon"`
- [public/apple-touch-icon.png](public/apple-touch-icon.png) — 180×180, linked as `rel="apple-touch-icon"`
Expand Down
Binary file modified public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 36 additions & 26 deletions scripts/export-logo.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// Export the /f visualization as a square PNG logo: unit box + the F
// polyline (seed 0.362 + 0.953i) — no ring, no glyph, no axes.
// polyline (seed 0.363 + 0.965i) — no ring, no glyph, no axes.
//
// Metrics match the page exactly: the plane scale is the page's
// min(w/3.6, h/2.35) at the 1440×900 reference viewport, lines are 2.6px,
// points 4px, with the page's stroke alphas (box 0.4, line 0.55,
// points 0.95). The canvas is sized from the scale so the drawing is
// pixel-identical to the live page.
// Geometry matches the page: the plane scale is the page's min(w/3.6, h/2.35)
// at the 1440×900 reference viewport, and the canvas is sized from it so the
// drawing lines up with the live page. Stroke weight does not — the export
// uses ONE stroke-to-image ratio across every size (logo + favicons), set to
// the Airbnb Bélo's line-to-height ratio, so the mark reads at the same
// boldness everywhere. And unlike the page — which
// layers a dimmer box under a brighter polyline — the export draws the whole
// mark in ONE mint tone via a single stroked path, so where the polyline
// crosses the box the coverage is unioned, not alpha-stacked: overlaps stay
// that flat color instead of darkening into a denser square.
//
// Also emits favicons (same composition, size-relative strokes and
// boosted alphas so the mark stays legible at tab size) into public/.
// Also emits favicons (same composition and stroke ratio, alpha boosted so
// the thinner small marks stay legible at tab size) into public/.
import { chromium } from "playwright";

const SPAN = 1.0; // half-extent shown — box (±1) is 0-margin, edge to edge
Expand All @@ -20,7 +25,7 @@ const faviconDir = process.env.FAVICON_OUT ?? "public";
const html = `<!doctype html><html><body style="margin:0">
<canvas id="logo" width="${SIZE}" height="${SIZE}"></canvas>
<script>
const F_RE = 0.362, F_IM = 0.953;
const F_RE = 0.363, F_IM = 0.965;
const MINT = "176,222,240";
const SPAN = ${SPAN};

Expand All @@ -37,9 +42,10 @@ const pts = [[F_RE, F_IM]];
}
}

// m: { line, alphaBox, alphaLine } — logo uses the page's exact stroke
// metrics; favicons scale them up for tab-size legibility. Lines only —
// the export draws no orbit points.
// m: { line, alpha } — one mint tone for the whole mark. The box and the F
// polyline share a single path stroked once, so overlaps are unioned into
// one flat color rather than compositing two translucent strokes into a
// darker square. Lines only — the export draws no orbit points.
function draw(size, withBg, m) {
const canvas = document.getElementById("logo");
canvas.width = size;
Expand All @@ -53,16 +59,13 @@ function draw(size, withBg, m) {
const s = size / (2 * SPAN);
const cx = size / 2, cy = size / 2;

// 0-margin box: inset by half the stroke so the full line width stays
// visible while the box hugs the image edge.
ctx.strokeStyle = "rgba(" + MINT + "," + m.alphaBox + ")";
ctx.lineWidth = m.line;
// Box + F polyline as ONE path, stroked once. 0-margin box: inset by half
// the stroke so the full line width stays visible while it hugs the edge.
const inset = m.line / 2;
ctx.strokeRect(cx - s + inset, cy - s + inset, 2 * (s - inset), 2 * (s - inset));

ctx.strokeStyle = "rgba(" + MINT + "," + m.alphaLine + ")";
ctx.strokeStyle = "rgba(" + MINT + "," + m.alpha + ")";
ctx.lineWidth = m.line;
ctx.beginPath();
ctx.rect(cx - s + inset, cy - s + inset, 2 * (s - inset), 2 * (s - inset));
pts.forEach(([re, im], i) => {
const x = cx + re * s, y = cy - im * s;
if (i === 0) ctx.moveTo(x, y);
Expand All @@ -71,14 +74,21 @@ function draw(size, withBg, m) {
ctx.stroke();
}

// Page-matched boldness: 2x the page stroke-to-box ratio (5.2px at the
// 766 box) — a PNG is typically viewed scaled down, which visually
// halves its strokes next to the live canvas.
const LOGO_METRICS = { line: 5.2, alphaBox: 0.4, alphaLine: 0.55 };
// Favicon metrics: strokes scale with size, alphas boosted.
// Unified stroke weight: ONE stroke-to-image ratio for every export, matched
// to the Airbnb Bélo — its uniform line measures 7.023 units on the symbol's
// 99.9-unit height in the official SVG (viewBox 320.1×99.9, symbol 92.8×99.9),
// ≈7.03% of the mark's height. Our box spans the image edge to edge, so the
// image size IS the mark height: same ratio at every size. Floored at 2px as
// a safety net (never hit at our sizes — 64px → 4.5px).
const STROKE_RATIO = 7.023 / 99.9; // Bélo parity ≈0.0703 → ≈54px on the 766 logo, 12.7px at 180, 4.5px at 64
const strokeFor = (size) => Math.max(2, size * STROKE_RATIO);
// One ink for every export: mint at 0.85 over #0a0a0a — the favicon/apple-touch
// brightness. A single alpha per mark also keeps box + F a flat, un-stacked color.
const INK_ALPHA = 0.85;
const LOGO_METRICS = { line: strokeFor(${SIZE}), alpha: INK_ALPHA };
const faviconMetrics = (size) => ({
line: Math.max(2, size * 0.035),
alphaBox: 0.7, alphaLine: 0.85,
line: strokeFor(size),
alpha: INK_ALPHA,
});

window.drawLogo = (withBg) => draw(${SIZE}, withBg, LOGO_METRICS);
Expand Down
Binary file modified src/assets/logo_f_box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/assets/logo_f_box_transparent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 12 additions & 10 deletions src/pages/f.astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
// by the unit box. Inside |z₀| = 1 the powers spiral to 0; outside they
// blow up.
//
// One seed is special: z₀ = 0.362 + 0.953i — at 3:41 in the video its
// polyline strokes the character F. A dashed ring marks it; whenever the
// cursor reaches it the whole drawing shifts from mint phosphor to amber.
// One seed is special: z₀ = 0.363 + 0.965i — a nudge off the video's 3:41
// value (≈0.362 + 0.953i), where the polyline strokes the character F. A
// dashed ring marks it; whenever the cursor reaches it the whole drawing
// shifts from mint phosphor to amber.
---
<!DOCTYPE html>
<html lang="en">
Expand Down Expand Up @@ -64,8 +65,8 @@
The angle doubling here is the germ of all that chaos.
</p>
<p>
At <span class="fn-math">z₀ = 0.362 + 0.953i</span> the polyline strokes an F
— the 3:41 moment of Numberphile's Mandelbrot video. Reach the ring
At <span class="fn-math">z₀ = 0.363 + 0.965i</span> the polyline strokes an F
a nudge off the 3:41 moment (≈0.362 + 0.953i) of Numberphile's Mandelbrot video. Reach the ring
(radius 0.035) and the drawing turns amber.
</p>
</aside>
Expand Down Expand Up @@ -134,10 +135,11 @@
</style>

<script>
// The ƒ-seed — z₀ whose squaring polyline strokes an F (Numberphile
// FFftmWSzgmk @ 3:41). Reaching it within LOCK_RADIUS re-colors the drawing.
const F_RE = 0.362;
const F_IM = 0.953;
// The ƒ-seed — z₀ whose squaring polyline strokes an F, tuned off the
// Numberphile 3:41 value ≈ 0.362 + 0.953i (FFftmWSzgmk). Reaching it within
// LOCK_RADIUS re-colors the drawing.
const F_RE = 0.363;
const F_IM = 0.965;
const LOCK_RADIUS = 0.035;

const MAX_STEPS = 32;
Expand Down Expand Up @@ -250,7 +252,7 @@
ctx.font = '10px "JetBrains Mono", monospace';
ctx.textBaseline = "top";
ctx.fillStyle = `rgba(${ink},0.85)`;
ctx.fillText("0.362 + 0.953i", fx + ringR + 5, fy + ringR + 4);
ctx.fillText("0.363 + 0.965i", fx + ringR + 5, fy + ringR + 4);
}

// Powers of z₀ by repeated squaring — connected points. Inside the
Expand Down