New SvelteKit + Sequoia website for faol with blog under /notes, timeline, tag filtering, TOC, and RSS
LandingLayout improvements (subtitle, expanded footer, @theme bridge), cross-references between all five tools, inline symbols replaced with Lucide icons, support links consolidated to ewancroft.uk/support
Replaced analytics taglines, removed duplicate footers from LandingPage and all about pages, deduplicated Tourmaline header navLinks
Moved Tourmaline from standalone repo into packages/tourmaline, enhanced landing-ui with backdrop blur, nav links, snippet props, and fixed source URLs across all projects
Moved the entire analysis pipeline from server-side POST to a browser web worker, fixing Vercel body size limits for large scrobble histories
Added View Transitions API crossfades, scroll-triggered section reveals, a dedicated support page, and seasonal colour theme lock-in
Added web OAuth import flow, post threading for long content, and TID-based record keys to the Twitter/Mastodon/Threads to Bluesky converter
Profile redesign with tabbed layout, listening sessions, on this day, story-mode recap, date range filtering, and listening evolution phases
Added OG hero SVGs to all five project landing pages, bismuth fetch flags, and devlog project to croft-click
Minutes listened, music evolution, remarkable days, discovery, and yearly wrapped card with scroll-reveal animations
Replaced astronomical season detection with traditional Wheel of the Year sabbat dates, added midnight auto-update and reset-to-seasonal
Added Opal documentation, eliminated 1.3MB client bundle from markdown/atproto deps, and added devlog link to sidebar
Listening clock, listening stats, and service origins
Added genre bars, mood indicators, stat badges, and dynamic SVG layout to the personality card
Era analysis, time-of-day mood weighting, genre blending, Gini-based loyalty, full-artist genre profile
Swapped unicode symbols for Lucide icon components across malachite, opal, jasper, bismuth, tourmaline, landing-ui, and croft-click
First release of tourmaline — personality archetypes, genre profiles, mood mapping, and share-to-Bluesky for Teal.fm scrobbles.
Why className and style props make your life harder, not easier
Building intuition for matrix transforms by building a rotating cube with Canvas2D APIs.
Convert richtext-block documents from the Standard.site ecosystem (Leaflet, Pckt, Offprint) to Markdown — available as both a CLI tool and a TypeScript library.
Generic deterministic value-noise generation. Arbitrary dimensions, multi-octave FBM, multiple colour modes. Zero dependencies, works in browsers and Node.js.
Deterministic value-noise avatar generation from a string seed — thin wrapper around @ewanc26/noise, works in browsers and Node.js.
Composable Svelte 5 components for an ATProto PDS landing page — terminal aesthetic, live status fetching, zero config to drop in.
Ewan's personal package monorepo — language-agnostic workspace with TypeScript, Rust, and Python packages.
Ewan's personal package monorepo — the canonical home for @ewanc26/tid, @ewanc26/atproto, @ewanc26/ui, and @ewanc26/utils.
Shared utility functions extracted from ewancroft.uk — date formatting, number formatting, URL helpers, validators, RSS generation, and locale detection.
AT Protocol service layer extracted from ewancroft.uk — profile fetching, Standard.site documents, Bluesky posts, music/mood status, Tangled repos, and a built-in in-memory cache.
Zero-dependency, spec-compliant AT Protocol TID generation for Node.js and browsers.
A simple Bluesky bot that posts random wolf noises at random intervals. Unmaintained.
CLI tool for syncing GitHub repositories to Tangled with ATProto record publishing — now part of the @ewanc26/pkgs monorepo.
A look at why the "x" in y syntax in TypeScript can lead to dead code.