Mangle Kuo

About Mangle Kuo

Design engineer in London. I build calm interfaces for live, messy data, like ssh-ldn noise maps, TfL transport boards and Where's My Flight planes outside your window.

Based in London

Drag the middle handle to resize

  • No moves left

    —

    No legal swipes left. The bars are Jev's last vote.

    Score
    0
    Best
    0
    Moves
    0

    No moves left. Score 0, best tile 0.

Selected work

Live, real-world data turned into maps, motion and tools. Some of it runs off my own hardware. Most started at a London hackathon, built with my own roster of AI agents working to written rules.

  • Travel ToolReact 19Server ActionsData UX

    Where's My Flight

    Inbound-aircraft delay tracing with FlightAware — Suspense boundaries, server actions, and leg switching without full reloads.

    Click to expand

    Where's My Flight pipeline: Flight number → Current flight → Tail history → Inbound leg → Delay context

    What it is

    A travel tool that follows the aircraft assigned to your flight number so you can see whether the inbound leg is late before the headline status updates.

    What I built

    A hybrid SSR/CSR app using React 19 `use()` with Server Actions, Suspense for inbound data, and `startTransition` so search and timeline changes stay responsive.

    Why it was hard

    Designing a text-first interface with conditional dimming, historical legs, and streaming inbound details while respecting API rate limits and keeping the story readable under load.

    Metrics

    Lighthouse 100 Performance/SEO.

    Implementation notes
    • Split the interface between server-rendered structure and client-side flight interaction so useful content appears early.
    • Used React 19 `use()`, Server Actions, Suspense boundaries, and `startTransition` to keep timeline/search interactions responsive.
    • Designed around real user stress behaviour: checking inbound aircraft, historical legs, and delay context before official status changes.
    • Kept the interface text-first and readable despite API latency, rate limits, and conditional flight data.
  • Map UXAccessibilityGeospatialHackathon

    ssh-ldn

    London noise map for renting, buying and visiting. Search an address, see what makes the noise, and ask follow-up questions by voice.

    ssh-ldn analysing 5 Euston Road with a 93/100 noise score, source cards, and a noise heatmap over King's Cross
    Click to expand

    ssh-ldn pipeline: Address / postcode → Geocode → DEFRA + OSM layers → Score + confidence → Map + voice explain

    What it is

    A map that makes city noise legible. You can already compare commute, crime and rent. ssh-ldn tells you whether a flat is likely to be quiet, and why, across weekdays, weekends, days and nights.

    What I built

    The whole flow from address to explanation, on Next.js 16 and MapLibre: geocoding, DEFRA road, rail and airport layers, OSM nightlife with time-of-day rules, a score card that shows its confidence, an ambient sound preview, ElevenLabs voice Q&A, and a public API and MCP endpoint.

    Why it was hard

    Strategic noise maps are yearly averages, not live readings. The work was showing uncertainty, time of day and competing sources honestly, while keeping a dense map fast to explore.

    Metrics

    Built in one day at Londonmaxxing 003 (Live London track). Live at ssh-ldn.app with search, layers, analysis and voice mode.

    Implementation notes
    • Started from the real gap: commute and rent are searchable; whether a home will sound liveable usually only shows up after moving in.
    • Layered DEFRA Round 4 strategic maps with OSM pubs, clubs, venues, and hospitals so local nightlife can outweigh a quiet road baseline after dark.
    • Froze the score format early (main sources, day/evening/night profile, confidence band, checks to do) so each demo address told a different story.
    • Designed accessibility into the product: voice mode describes sound environments in language, not only colour on a map.
    • Shipped a public read-only API and MCP tool so the same address-to-noise explanation can be reused outside the UI.
  • HackathonAI AgentsGamesBenchmark

    Astra vs Human

    Same board, same taps. A mini-game benchmark where you race an AI agent, or watch agents race each other. Built at the GPT-6 Astra hack.

    astra vs human / preview
    Click to expand

    What it is

    Humans and agents play the same public puzzle boards with the same taps. One game is five boards in a row, ten minutes each.

    What I built

    The board families, You vs Agent and Agent vs Agent modes, and the agent side. You can pit yourself against Jev and Laya, a new family of decision models, against an OpenAI model, or against both working together.

    Why it was hard

    Keeping it fair. The agent sees only the board you see and answers with one cell or a short burst of taps, so speed and judgement are compared on equal terms.

    Implementation notes
    • Gave every player, human or agent, the same public board, so the result measures play, not access.
    • Kept decision models and the OpenAI model behind one agent picker, so the same match can be replayed against either, or both together.
  • Hackathon WinnerNode EditorData PipelinesDev Tool

    CompileFlow

    A node-graph editor that compiles live URLs into tested scrapers. Hackathon winner.

    compileflow / preview
    Click to expand

    What it is

    Point it at a live page, wire up nodes, and it compiles the graph into a scraper with tests.

    What I built

    Details to follow.

    Why it was hard

    Details to follow.

    Implementation notes
    • Details to follow.
  • Design SystemOpen SourceTransport UINext.js

    tfl-ts + TfL components

    A typed TfL API client and a React component registry for London transport boards. Copy the source, own it, run the Board on an iPad.

    Click to expand

    tfl-ts + TfL components pipeline: TfL Unified API → tfl-ts types + hubs → Normalised rows → Data-aware board → Primitives + colours → iPad Board / your app

    What it is

    TfL publishes a free, rate-limited Unified API. The visual language of London transport is a different problem: official line colours, a trademarked roundel, Johnston-adjacent type, and operational quirks the JSON will not explain. tfl-components is that visual layer. You copy the React source into your app. tfl-ts supplies the typed data. The hosted Board at tfl.manglekuo.com turns any modern browser into a wall display if you do not want to ship an app.

    What I built

    Two repos that are meant to work together. tfl-ts is a zero-dependency TypeScript client generated from TfL's OpenAPI snapshot: friendly wrappers, 84 raw endpoints, static station topology, colours, and severity helpers. tfl-components is a shadcn registry, not an npm UI package, so consumers own the source. The docs site is the developer environment: live boards, Explorer, maps, and a Board builder whose config lives in the URL hash.

    Why it was hard

    The API will happily return the wrong StopPoint. Liverpool Street is several ids; poll the hub interchange and you get nothing. Circle, Hammersmith & City, and Metropolitan trains share metal and then do not, so a board that paints the raw lineId as gospel lies at Victoria and at Baker Street in different ways. Dark-mode Northern is brand black: a white fill would erase the identity, so contrast is a hard outline. Arrivals tiles are 48px and must never grow, while station names still have to be findable when they wrap or abbreviate.

    Metrics

    Registry v0.7.0 and tfl-ts 2.11.0 on npm, with about 300 weekly downloads. As of August 2026 the live site reported about 115 component installs.

    Implementation notes
    • Split the typed client from the React boards so an API package never drags in a UI stack, and so consumers copy source with shadcn instead of depending on a sealed component package.
    • Organised the docs around developer intent (status, arrivals, maps, diagrams) rather than TfL's legacy endpoint taxonomy.
    • Shipped STATION_HUBS in tfl-ts so boards resolve the sibling StopPoint that actually carries arrivals, instead of asking visitors to learn NaPTAN.
    • Grouped Circle / Hammersmith & City / Metropolitan arrivals by shared-track topology. The raw lineId can flip along the same train.
    • Locked arrivals to a 48px tile rhythm so boards stay aligned side by side. StationName still keeps find, copy, and screen readers on the full name when the paint abbreviates.
    • Defaulted the roundel to a filled placeholder. Production builds stay silent; the official SVG is an explicit opt-in because the mark is trademarked.
  • Open SourceHardwareE-inkNext.js

    byos-nextjs

    A self-hosted server for the TRMNL e-ink display, built in Next.js. Now also kept under TRMNL's own org as byos_next.

    byos nextjs / preview
    Click to expand

    What it is

    TRMNL is a small e-ink screen that pulls its picture from a server. byos-nextjs is one you run yourself: manage devices and render screens in React, deployed on Vercel with Supabase.

    What I built

    The server, the device management UI and the screen rendering pipeline, packaged so anyone can deploy their own copy to Vercel in one click.

    Why it was hard

    An e-ink panel wants a crisp 1-bit image on a schedule, not a web page. Every screen has to be designed in React and still come out clean at that resolution.

    Implementation notes
    • Kept it deployable in one click to Vercel with Supabase, so owning your server is not a weekend project.
    • Moved the canonical copy to the usetrmnl org as byos_next, so issues and contributions go to one place.
  • Visual ToolWeb WorkerThree.jsTool

    tsl-dither

    A browser image editor for retro dithering. Bright P3 dots on black, per-channel noise, and a worker that keeps the sliders smooth.

    Click to expand

    tsl-dither pipeline: Load image → Downsize → Tone map → Canvas preview → Worker dither → Export

    What it is

    A five-step editor (Load, Downsize, Tone, Dither, Export) for 8-bit style dithering in vivid magenta, yellow and blue on black. On high-contrast photos the dots float in the shadows like a lidar scan.

    What I built

    The pipeline UI: canvas previews, staged downscaling, and a Web Worker for tone and dither. Each colour channel gets its own noise before thresholding, which breaks up the Bayer grid but keeps gradients readable.

    Why it was hard

    In a 2D browser editor the bottleneck is the main thread, not the GPU. Moving work to a worker kept the sliders smooth where GPU experiments did not.

    Metrics

    Sliders stay smooth while large images process in a worker.

    Implementation notes
    • Split the editor into stages: Load, Downsize, Tone, Dither, Export, so expensive image work stays understandable and recoverable.
    • Landed on per-channel white noise (independent R/G/B noise, threshold each, recombine) over pure threshold or ordered Bayer dithering.
    • Kept canvas preview separate from Web Worker dithering to avoid blocking interaction while processing megapixel buffers.
    • TSL/WebGPU was the original direction; settled on Three.js canvas plus a worker because thread isolation mattered more for 2D photo editing.

Showcase

Small component studies. Hairlines, dimming, easing that never restarts.

Browse all