Skip to content

Serve the hand-drawn blog art as traced SVG - #209

Merged
kixelated merged 4 commits into
mainfrom
quest/m2/site-svg
Oct 28, 2026
Merged

kixelated merged 4 commits into
mainfrom
quest/m2/site-svg

Conversation

@kixelated

@kixelated kixelated commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

The blog's hand-drawn diagrams and sketches are now SVG instead of PNG. They sit on a transparent background in the site palette, so each one is drawn straight onto the page at any resolution. Social cards still get a PNG.

Changes

  • 55 drawings traced, counting the author avatar and kixelCat. Every reference now points at the SVG, including robotics.mdx's reuses, the frontmatter covers, and components/author.astro. The PNGs are gone. Together the SVGs come to 1.4 MB against the PNGs' 7.5 MB (about 5.5x smaller), and every one is smaller than its PNG. The quic-powers grey boxes (#292929/#212121) are gone too.
  • tools/drawn/trace.ts replaces tools/icons/smooth.sh, exposed as just trace. It handles drawn PNGs and the existing icon SVGs (SIGMA=12 just trace --crop public/drawn/icon-*.svg reproduces the homepage icons). Each colour is taken from the drawing's flat regions and snapped to the palette (#fafafa, #00be2d, #a3a5a8, black) when it is within reach; accent and fill colours keep their own value. Each colour layer is then upscaled, blurred, thresholded, and run through potrace. The viewBox stays at the original pixel size, so layout does not shift.
    • Antialiased pixels are split between the two colours they blend, so edges stay subpixel against the background and between inks.
    • White over slate blends almost exactly to the site grey. Colour regions that are thin everywhere (halos) go to the neighbouring colour, so white strokes don't get grey outlines.
  • SVG covers render to PNG at build time. src/pages/og/[...cover].png.ts renders each SVG cover with sharp at 1200px wide, on slate-900. global.astro points og:image there. The SVG is the only committed source. sharp is now an explicit devDependency (it already came in through Astro).
  • imagemagick, librsvg, and potrace are in the dev shell. public/icons/README.md and AGENTS.md (one line, written against the trimmed shape from moq-dev/moq.dev#200) point at the recipe.
  • Deletes the quest and its link from the m2 README.

Choices made without asking

  • Left raster: first-app/demo.png (screenshot), hash-tag-community/hash.png and open-for-business/logo-full-dark.png (logos, not drawings), and to-wasm/slide.png (pasted bitmaps, per the plan). Every filled drawing the plan names (vegeta, kixelCat, narwhal, boat, cloudflare, moqbs) traced without visible loss, so none fell back to PNG.
  • Deleted cars2/toilet.png and open-for-business/bean.png instead of tracing them. No commit in the history has ever referenced them.
  • Black is a palette colour. Many drawings have real black fills (McQueen's windows and mouth, Wario's moustache, Vegeta's hair), not transparency, so they stay black on slate.
  • Ena's dark grey hair (#272727) is kept even though it sits close to slate. The background only absorbs its own noise. Antialiased fringes are never flat, so they never become a colour.
  • og:image is 1200px wide for every SVG cover, the width large cards are laid out at. The old PNG covers were their native size: kixelCat was 240px, small enough that scrapers showed it as a thumbnail.
  • Cropping is now opt-in (--crop). smooth.sh always cropped, which drawings can't allow. Icons re-run through the tool would get #fafafa instead of #fcfcfc for their white, which is not visibly different. No icon was re-traced in this PR.

Verification

  • just check, bun test, bun astro build --mode live, and bun scripts/check-og.ts pass (social cards OK across 38 pages; og:image for e.g. cars2 is https://moq.dev/og/blog/cars2/queen.png, 1200x675).
  • Every pair was compared side by side (PNG on slate against SVG on slate) at 1x. A few were also checked at 2x, and filled shapes and boundaries at 8x.
  • Checked in just dev at deviceScaleFactor 1 and 2 with Playwright: quic-powers tuple-quic2 and vegeta, cars2 queen, open-for-business ena, and the author avatar.
  • Looked at the built cover PNGs for cars2 and kixelCat.

Follow-ups

  • The two raster logos (hash.png, logo-full-dark.png) could come from their original vector sources, if any exist. Out of scope here.
  • scripts/check-og.ts could also assert that every SVG cover has its rendered PNG. Today the build fails loudly if one is missing, which already covers it.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

kixelated and others added 4 commits October 27, 2026 22:58
Quest-Claim: 13d52a465828ab9d
Trace 55 hand-drawn PNGs into flat colour layers on a transparent
background, snapped to the site palette, and point every reference at
them. tools/drawn/trace.ts replaces tools/icons/smooth.sh as the one
tracer for drawings and icons, exposed as `just trace`. SVG covers are
rendered to PNG at build time for og:image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Hand-drawn blog art as SVG Serve the hand-drawn blog art as traced SVG Oct 28, 2026
@kixelated
kixelated marked this pull request as ready for review October 28, 2026 06:21
@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 36 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 76c59b3d-0cc0-4b19-acd7-42204831729c
Commits

Reviewing files that changed from the base of the PR and between b76b1e1 and c3b816c.

Files ignored due to path filters: 113
  • bun.lock is excluded by !**/*.lock
  • public/blog/application-first/ai.png is excluded by !**/*.png
  • public/blog/application-first/ai.svg is excluded by !**/*.svg
  • public/blog/application-first/step.png is excluded by !**/*.png
  • public/blog/application-first/step.svg is excluded by !**/*.svg
  • public/blog/application-first/sun.png is excluded by !**/*.png
  • public/blog/application-first/sun.svg is excluded by !**/*.svg
  • public/blog/avatar.png is excluded by !**/*.png
  • public/blog/avatar.svg is excluded by !**/*.svg
  • public/blog/be-the-outlier/narwhal.png is excluded by !**/*.png
  • public/blog/be-the-outlier/narwhal.svg is excluded by !**/*.svg
  • public/blog/cars2/backfill.png is excluded by !**/*.png
  • public/blog/cars2/backfill.svg is excluded by !**/*.svg
  • public/blog/cars2/fanout.png is excluded by !**/*.png
  • public/blog/cars2/fanout.svg is excluded by !**/*.svg
  • public/blog/cars2/mom.png is excluded by !**/*.png
  • public/blog/cars2/mom.svg is excluded by !**/*.svg
  • public/blog/cars2/queen.png is excluded by !**/*.png
  • public/blog/cars2/queen.svg is excluded by !**/*.svg
  • public/blog/cars2/speed2.png is excluded by !**/*.png
  • public/blog/cars2/speed2.svg is excluded by !**/*.svg
  • public/blog/cars2/toilet.png is excluded by !**/*.png
  • public/blog/demoqed/demoqed.png is excluded by !**/*.png
  • public/blog/demoqed/demoqed.svg is excluded by !**/*.svg
  • public/blog/first-cdn/cloudflare.png is excluded by !**/*.png
  • public/blog/first-cdn/cloudflare.svg is excluded by !**/*.svg
  • public/blog/first-cdn/global.png is excluded by !**/*.png
  • public/blog/first-cdn/global.svg is excluded by !**/*.svg
  • public/blog/kixelCat.png is excluded by !**/*.png
  • public/blog/kixelCat.svg is excluded by !**/*.svg
  • public/blog/moq-boy/bren.png is excluded by !**/*.png
  • public/blog/moq-boy/bren.svg is excluded by !**/*.svg
  • public/blog/moq-boy/bren2.png is excluded by !**/*.png
  • public/blog/moq-boy/bren2.svg is excluded by !**/*.svg
  • public/blog/moq-boy/moqboy.png is excluded by !**/*.png
  • public/blog/moq-boy/moqboy.svg is excluded by !**/*.svg
  • public/blog/moq-boy/pub1.png is excluded by !**/*.png
  • public/blog/moq-boy/pub1.svg is excluded by !**/*.svg
  • public/blog/moq-boy/pub2.png is excluded by !**/*.png
  • public/blog/moq-boy/pub2.svg is excluded by !**/*.svg
  • public/blog/moq-onion/cancel.png is excluded by !**/*.png
  • public/blog/moq-onion/cancel.svg is excluded by !**/*.svg
  • public/blog/moq-onion/karp.png is excluded by !**/*.png
  • public/blog/moq-onion/karp.svg is excluded by !**/*.svg
  • public/blog/moq-onion/onion.png is excluded by !**/*.png
  • public/blog/moq-onion/onion.svg is excluded by !**/*.svg
  • public/blog/moq-onion/sfu.png is excluded by !**/*.png
  • public/blog/moq-onion/sfu.svg is excluded by !**/*.svg
  • public/blog/moq-onion/track.png is excluded by !**/*.png
  • public/blog/moq-onion/track.svg is excluded by !**/*.svg
  • public/blog/moqbs/moqbs.png is excluded by !**/*.png
  • public/blog/moqbs/moqbs.svg is excluded by !**/*.svg
  • public/blog/on-a-boat/boat.png is excluded by !**/*.png
  • public/blog/on-a-boat/boat.svg is excluded by !**/*.svg
  • public/blog/on-a-boat/fanout.png is excluded by !**/*.png
  • public/blog/on-a-boat/fanout.svg is excluded by !**/*.svg
  • public/blog/on-a-boat/multipath.png is excluded by !**/*.png
  • public/blog/on-a-boat/multipath.svg is excluded by !**/*.svg
  • public/blog/open-for-business/bean.png is excluded by !**/*.png
  • public/blog/open-for-business/ena.png is excluded by !**/*.png
  • public/blog/open-for-business/ena.svg is excluded by !**/*.svg
  • public/blog/open-for-business/imadethis.png is excluded by !**/*.png
  • public/blog/open-for-business/imadethis.svg is excluded by !**/*.svg
  • public/blog/open-for-business/reality.png is excluded by !**/*.png
  • public/blog/open-for-business/reality.svg is excluded by !**/*.svg
  • public/blog/quic-powers/anycast.png is excluded by !**/*.png
  • public/blog/quic-powers/anycast.svg is excluded by !**/*.svg
  • public/blog/quic-powers/anycast2.png is excluded by !**/*.png
  • public/blog/quic-powers/anycast2.svg is excluded by !**/*.svg
  • public/blog/quic-powers/http.png is excluded by !**/*.png
  • public/blog/quic-powers/http.svg is excluded by !**/*.svg
  • public/blog/quic-powers/quic.png is excluded by !**/*.png
  • public/blog/quic-powers/quic.svg is excluded by !**/*.svg
  • public/blog/quic-powers/standards.png is excluded by !**/*.png
  • public/blog/quic-powers/standards.svg is excluded by !**/*.svg
  • public/blog/quic-powers/tcp.png is excluded by !**/*.png
  • public/blog/quic-powers/tcp.svg is excluded by !**/*.svg
  • public/blog/quic-powers/tuple-quic.png is excluded by !**/*.png
  • public/blog/quic-powers/tuple-quic.svg is excluded by !**/*.svg
  • public/blog/quic-powers/tuple-quic2.png is excluded by !**/*.png
  • public/blog/quic-powers/tuple-quic2.svg is excluded by !**/*.svg
  • public/blog/quic-powers/tuple-quic3.png is excluded by !**/*.png
  • public/blog/quic-powers/tuple-quic3.svg is excluded by !**/*.svg
  • public/blog/quic-powers/tuple-tcp.png is excluded by !**/*.png
  • public/blog/quic-powers/tuple-tcp.svg is excluded by !**/*.svg
  • public/blog/quic-powers/tuple-tcp2.png is excluded by !**/*.png
  • public/blog/quic-powers/tuple-tcp2.svg is excluded by !**/*.svg
  • public/blog/quic-powers/tuple-webrtc.png is excluded by !**/*.png
  • public/blog/quic-powers/tuple-webrtc.svg is excluded by !**/*.svg
  • public/blog/quic-powers/tuple-webrtc2.png is excluded by !**/*.png
  • public/blog/quic-powers/tuple-webrtc2.svg is excluded by !**/*.svg
  • public/blog/quic-powers/vegeta.png is excluded by !**/*.png
  • public/blog/quic-powers/vegeta.svg is excluded by !**/*.svg
  • public/blog/tls-and-quic/warning.png is excluded by !**/*.png
  • public/blog/tls-and-quic/warning.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-still-the-problem/context.png is excluded by !**/*.png
  • public/blog/webrtc-is-still-the-problem/context.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-still-the-problem/mics.png is excluded by !**/*.png
  • public/blog/webrtc-is-still-the-problem/mics.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-still-the-problem/navi.png is excluded by !**/*.png
  • public/blog/webrtc-is-still-the-problem/navi.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-the-problem/bleach.png is excluded by !**/*.png
  • public/blog/webrtc-is-the-problem/bleach.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-the-problem/car.png is excluded by !**/*.png
  • public/blog/webrtc-is-the-problem/car.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-the-problem/chad.png is excluded by !**/*.png
  • public/blog/webrtc-is-the-problem/chad.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-the-problem/claude.png is excluded by !**/*.png
  • public/blog/webrtc-is-the-problem/claude.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-the-problem/rtt.png is excluded by !**/*.png
  • public/blog/webrtc-is-the-problem/rtt.svg is excluded by !**/*.svg
  • public/blog/webrtc-is-the-problem/trenchcoat.png is excluded by !**/*.png
  • public/blog/webrtc-is-the-problem/trenchcoat.svg is excluded by !**/*.svg
Files selected for processing: 29
  • AGENTS.md
  • flake.nix
  • justfile
  • package.json
  • public/icons/README.md
  • quest/m2/README.md
  • quest/m2/site-svg.md
  • src/components/author.astro
  • src/layouts/global.astro
  • src/pages/blog/application-first.mdx
  • src/pages/blog/be-the-outlier.mdx
  • src/pages/blog/cars2.mdx
  • src/pages/blog/demoqed.mdx
  • src/pages/blog/distribution-at-twitch.mdx
  • src/pages/blog/first-cdn.mdx
  • src/pages/blog/moq-boy.mdx
  • src/pages/blog/moq-onion.mdx
  • src/pages/blog/moqbs.mdx
  • src/pages/blog/on-a-boat.mdx
  • src/pages/blog/open-for-business.mdx
  • src/pages/blog/quic-powers.mdx
  • src/pages/blog/tls-and-quic.mdx
  • src/pages/blog/update-01.mdx
  • src/pages/blog/webrtc-is-still-the-problem.mdx
  • src/pages/blog/webrtc-is-the-problem.mdx
  • src/pages/og/[...cover].png.ts
  • src/pages/robotics.mdx
  • tools/drawn/trace.ts
  • tools/icons/smooth.sh
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-28T06:22:30.024735Z c3b816c Draft marked ready
🔒 Security Review ✅ Completed 2026-10-28T06:25:01.270835Z c3b816c Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated

Copy link
Copy Markdown
Collaborator Author

Landing summary:

  • Converts the hand-drawn blog art to SVG. Choices accepted by the maintainer: 4 images stay PNG, 2 never-referenced PNGs are deleted, black fills stay black, and covers are 1200px wide.
  • CI passed and Codex reported no findings on c3b816c.
  • quest check --base origin/main passes on this head; the PR finishes quest/m2/site-svg.
  • Squash-landing c3b816c.

(Written by Opus 5.5)

@kixelated
kixelated merged commit 682f766 into main Oct 28, 2026
3 checks passed
@kixelated
kixelated deleted the quest/m2/site-svg branch October 28, 2026 06:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant