04

Projects

Work I've shipped and the side projects I keep alive on the weekends.
$ls -lah ./projects
>8 entries · sorted by impact
tech demos for these projects
liveAI-built2026

Reach · 可达 · Melbourne transit atlas

the whole city recolours as you drag the pin · RAPTOR written from scratch · checked against brute-force oracles · English / 中文

// ai-built — Planned, written and tested largely by Claude Code agents (Anthropic’s coding agents) working as a team of builders and independent verifiers, directed and reviewed by Jun.

reach.juncao.devnight · en
Reach in its Night theme: travel times by public transport from Monash University (Clayton), shaded in ten-minute bands across Melbourne, beside the panel of origin, departure time, transport modes and walking settings.

Reach shows everywhere you can get to by public transport in Melbourne and Victoria, door to door, for the day and time you choose. Drop or drag the pin and the whole city recolours in ten-minute bands. Underneath is a RAPTOR routing engine written from scratch over Transport Victoria’s full GTFS timetable. Around the map: journeys that explain themselves, arrive-by, the last tram home, a meeting point for up to five people, two homes compared, a whole day played back, and an algorithm theater. The engine is tested against independently written brute-force oracles, and the app is bilingual (English / 中文).

  • ▸RAPTOR from scratch over the full feed: metro trains, trams, buses, V/Line trains and coaches, regional buses and SkyBus. The current timetable compiles to 28,692 stops and 376k trips in one checksummed binary file, which a pool of worker threads reads in place from a SharedArrayBuffer.
  • ▸Fast enough to follow a drag: a one-to-all query (the earliest arrival at every stop) takes about 11 ms at p95 inside a worker on my i9-10900K, and live queries are latest-wins, so the map keeps up while the pin moves.
  • ▸Checked, not assumed: 1,000 queries over the real timetable compare about 205 million labels with brute-force oracles that share no code with the engine (a naive scan of every trip, a time-expanded graph) and with a time-dependent Dijkstra baseline: 0 mismatches. There are also 19 fast-check properties over 12,900 generated cases and 24 golden journeys.
  • ▸Journeys that explain themselves: the fastest option, any slower one that needs fewer rides, and walking, each with its legs, platforms and waits and a plain-language reason it is listed (sixteen kinds, from a tight change to a Night Network service). A “Best time to leave” chart covers every departure minute of the next two hours.
  • ▸The map is a travel-time field drawn per pixel on the GPU: WebGL2 renders a cone for every reached stop into a depth buffer, so the depth test keeps the minimum, then shades ten-minute bands with analytic isolines. A self-test against the CPU runs before the GPU is trusted, and each new answer flows into place instead of jumping.
  • ▸Eight modes in one app (Reach, Journey, Arrive by, Last tram home, Compare, Meet-up, Play the day and an algorithm theater that replays RAPTOR’s rounds beside a Dijkstra), Night and Paper themes, an interactive user guide and a recorded demo.
  • ▸Release gates: Playwright end-to-end tests in Edge and Chrome, a pixel-level layout audit across both themes and languages at desktop, tablet and phone sizes, and a Proof page in the app with the full verification report and benchmarks.
  • ▸Built in about ten days. A lead agent froze the contracts and split the work into tracks that each own their files; builder agents worked in parallel and separate verifier agents checked their work.
● architecture · ascii
+-------------+  https  +------------------+  :80   +------------------+
|   Browser   |<------->|    CloudFront    |<------>|   nginx · EC2    |
| MapLibre GL |   wss   | reach.juncao.dev | secret | t4g.small arm64  |
|  GPU field  |         | cached map tiles | header | rate limits · ws |
+-------------+         +------------------+        +--------+---------+
                                                             | loopback
                                                             v
+--------------------+  task / result  +-----------------------------+
|    worker pool     |<--------------->|  Fastify server             |
| RAPTOR · journeys  |                 |  HTTP + WebSocket · live    |
+---------+----------+                 |  queries are latest-wins    |
          | reads in place             +-----------------------------+
          v
+---------------------------------------------+
| SharedArrayBuffer timetable                 |
| 28,692 stops · 376k trips · 14M stop events |
+---------------------------------------------+
TypeScriptFastifyworker_threadsSharedArrayBufferReactMapLibre GLWebGL2PMTilesGTFSfast-checkPlaywrightAWS EC2 · CloudFrontClaude Code
live2026

Resume Agent · AI resume tailoring studio

multi-model Bedrock rewriting · layout-preserving .docx edits · real page-limit enforcement · live on this domain

A full-stack AI studio that tailors a resume and cover letter to a specific job description — deployed right here at juncao.dev/resume-agent. Upload a .docx, paste the JD, pick a model (Claude or GPT-OSS via Amazon Bedrock), and it either regenerates polished markdown or rewrites your original document paragraph-by-paragraph at the OOXML level, so the layout you designed survives untouched. Entirely serverless — Lambda + DynamoDB + S3 behind this site's CloudFront — with per-user credit billing in AUD and an auth stack built from scratch.

  • ▸Multi-model generation on Amazon Bedrock — Claude Haiku / Sonnet / Opus plus GPT-OSS — through one streaming runner with retries, model fallback, and effort-tiered extended-thinking budgets.
  • ▸Keep-your-layout mode: the original .docx is segmented and edited in place at the OOXML level (runs, numbering, tables, hyperlinks preserved) — the model returns only per-paragraph changes, never a reformatted document.
  • ▸Measured page-limit enforcement: output is rendered to PDF (headless Chromium / LibreOffice in a container Lambda), pages are counted, and the model gets up to two 'cut N lines' repair rounds until the document actually fits the limit your rules asked for.
  • ▸Credit billing in AUD: reserve → settle ledger in DynamoDB with per-model token prices, cache read/write rates, and FX + markup from SSM — every run shows its real cost down to the micro-cent.
  • ▸Auth from first principles: scrypt password hashing, ES256 JWTs, rotating refresh-token families with reuse detection, and lockout with exponential backoff.
  • ▸Poll-based job pipeline built for Lambda's limits — async worker, DynamoDB event log with TTLs, heartbeat stall detection, idempotent billing settlement; inputs autosave and every generation lands in a 180-day S3 history.
  • ▸Bilingual UI (English / 中文), a prompt preset library, and an AI assistant that proposes prompt updates via tool-use.
● architecture · ascii
+------------+  /resume-agent/*  +------------+   async   +------------+
| CloudFront |------------------>|   API fn   |---------->| Worker fn  |
| juncao.dev |  /api/* (strip)   | Hono + JWT |           |  Bedrock   |
+-----+------+                   +-----+------+           +-----+------+
      |                                |                        |
      v                                v                        v
+------------+              +------------------+      +----------------+
|   Web S3   |              |     DynamoDB     |      |  Renderer fn   |
| SPA bundle |              |  jobs · ledger · |      |  LibreOffice + |
+------------+              | events · history |      |  Chromium PDF  |
                            +------------------+      +----------------+
TypeScriptReactHonoAWS LambdaDynamoDBS3CloudFrontAmazon BedrockAWS CDKDockerOOXMLSES
personal2026

Sudoku · with animated backtracking solver

the LeetCode 37 question I bombed in an interview, kept on the site as a reminder

Built this because I bombed the Sudoku Solver question (LeetCode 37) in an interview. It's a fully playable 9x9 sudoku game at three difficulties, but the real point is the second tool: an auto-solver that runs the same backtracking algorithm I should have written, animated cell-by-cell so you can watch it try, commit, and backtrack live. Also ships a hint tool that lists every legal digit for the cell you've selected (per row, column, and 3x3 box).

  • ▸Random valid puzzle generation with rotational symmetry on cell removal — easy / medium / hard difficulties.
  • ▸Hint tool: for the selected cell, lists every digit 1-9 that satisfies the row + column + 3x3 box rules right now.
  • ▸Animated backtracking solver — generator-based, slider-controlled step interval (2-50 ms), color-coded try / commit / backtrack states, live counters for tries / commits / backtracks.
  • ▸Smart auto-solve: tries to honour your entries first; only clears them if your placements rule out every solution. Given cells are never mutated.
  • ▸Show-solution shortcut, move counter, indicator toggle for a clean board view.
TypeScriptReactBacktrackingGenerators
open widget
personal2026

Algorithm Visualizer · sorts + your own Python

15 built-in sorts · side-by-side compare · run your own algorithm in real CPython

Two-tab algorithm playground. Tab one ships 15 sorting algorithms (popularity-ordered) with bar-chart animation, live op-counters (compares / reads / writes / swaps), per-algorithm complexity badges, a 'how it works' write-up, and source code in Python / JavaScript / Java / C#. Tab two is a real-CPython sandbox where you write your own sort and the runtime tracks every operation.

  • ▸15 sorting algorithms — Quick, Merge, Heap, Insertion, Selection, Bubble, Tim, Shell, Counting, Radix, Bucket, Comb, Cocktail, Gnome, Pancake — each with executable JS that emits a shared op-stream consumed by the visualizer.
  • ▸Side-by-side compare mode: pick any two algorithms, run them on identical input, watch the difference in op counts.
  • ▸User-code tab: real CPython via Pyodide (loaded in a Web Worker, lazy-init'd, ~10 MB first-load).
  • ▸Modern editor (CodeMirror 6) with Python syntax highlighting, line numbers, autocomplete, and live `compile()`-based syntax checking.
  • ▸Custom TrackedList Python class proxies the user's array — every read, write, and comparison drives the same animated bar chart.
  • ▸15 test cases (incl. empty, single, sorted, reversed, all-equal, negatives, dupes, near-sorted, n=200 adversarial). Validates against sorted(input). All run output streams into a console panel.
  • ▸Crude complexity bucketer: re-runs your algo at n = 50/100/200/400/800, fits ratios to log / linear / nlogn / quadratic / cubic models, displays the closest match.
TypeScriptReactCodeMirror 6Web WorkerPyodide (CPython in browser)
open widget
personal2024 — present

Price Sentinel

Scheduled price-watcher with email alerts and a plugin model

A self-hosted, extensible price monitor I built for myself. Tracks any number of products across multiple sites, runs on a schedule, and emails me the moment a price drops below a target I set. New sites are added by writing a tiny scraper plugin — no code changes elsewhere.

  • ▸Add a watch in one CLI command or via a small web form: URL, target price, optional expiry.
  • ▸Pluggable scraper interface — drop a `sites/<store>.py` file implementing `parse(html) -> price` and the scheduler picks it up.
  • ▸Per-item cooldown so you don't get alert-spammed when a price hovers around the threshold.
  • ▸Time-bound watches (e.g. 'remind me only until Black Friday') with a daily expiry sweeper.
  • ▸Pluggable notifier: SMTP email by default, optional Telegram bot and webhook.
  • ▸Runs anywhere: a single Docker container, or AWS Lambda + EventBridge for a free-tier serverless deploy.
  • ▸DynamoDB / SQLite storage adapter — start local, graduate to cloud without code changes.
● architecture · ascii
+--------------+    cron     +----------------+    fetch    +--------------+
|  Scheduler   |------------>|  Watcher core  |------------>|   Scrapers   |
|  EventBridge |             |    (Python)    |             |   plugins/   |
|   / cron     |             |                |<---price----|  amazon.py   |
+--------------+             +--------+-------+             +--------------+
                                      |
                                      v compare
                             +----------------+    alert    +--------------+
                             |    Watch DB    |------------>|   Notifier   |
                             |  (DDB / SQL)   |             | email/tg/wh  |
                             +----------------+             +--------------+
PythonBeautifulSouphttpxAPSchedulerDynamoDB / SQLiteDockerAWS LambdaEventBridgeSMTPTelegram Bot
shipped2023.07 — 2023.12

Job Portal for New Immigrants

Serverless job portal with AI-powered recommendations

A serverless job portal supporting new immigrants in finding employment in Australia. Backend services on AWS Lambda + Python improved scalability, availability, and user experience.

  • ▸Designed scalable backend architecture with AWS Lambda + API Gateway — low-cost and high availability.
  • ▸Implemented an AI-based recommendation engine using vector search to improve job match accuracy.
  • ▸Configured Route 53 + CloudFront to optimise routing and reduce latency.
PythonAWS LambdaAPI GatewayRoute 53CloudFrontVector SearchServerless Framework
live2024 — present

Visa Form Automation

End-to-end form-fill, review, and e-lodgement pipeline

Owned and led the backend behind Visa Robot's flagship form-automation flow — collecting client data, automatically populating government PDF/HTML forms, routing through a review pipeline, and e-lodging once approved.

  • ▸Domain-driven service split: intake, mapping, validation, review queue, e-lodgement.
  • ▸Idempotent state machine for each application — survives Lambda timeouts and retries.
  • ▸Audit log of every field mutation and reviewer decision.
  • ▸Recognised internally with the CodeBlack Best Backend Engineer award after delivery.
PythonAWS LambdaDynamoDBS3Step FunctionsAPI Gateway
▸ls -lah ./projects