Inspiration
Every year, emergency departments across the country face a quiet, recurring crisis: boarding. Patients who need a bed wait in hallways because the ED is full, while somewhere down the hall a bed sits empty during its turnover window, or a float nurse who could help is sitting unused two floors up. The problem usually isn't a lack of resources — it's a lack of visibility into where those resources are and when they're about to run out.
We kept coming back to an aviation metaphor: air traffic controllers don't wait for two planes to nearly collide before acting — they see the trajectory and reroute early. We wanted to build that same kind of system for a hospital floor: a live "control tower" that watches capacity trends, projects where the system is headed, and tells staff exactly what lever to pull — pull a float nurse, open an overflow bed, or relocate a patient — before a Tier 1–2 patient (the sickest, most time-critical arrivals) ever misses their target wait. That's where the nameWayPoint Allocations came from: a waypoint is a point you navigate toward, not one you react to after you've already passed it.
What We Learned
- Queueing systems break in non-obvious ways. Our first working version of the discharge engine looked reasonable in code but pinned every bed at 100% occupancy forever, because it modeled discharges as a flat "release N patients every 5 seconds" batch process instead of a real distribution of individual stay lengths. The fix — and the lesson — was Little's Law intuition made concrete:
$$ L = \lambda W $$
the number of patients in the system ($L$) is the arrival rate ($\lambda$) times the average time each one stays ($W$). Our old model had no $W$ per patient at all — just a shared clock — so the system could never reach a steady state. Once each patient was given their own length of stay drawn from a tier-based distribution,
$$ \text{LOS}{\text{tier}} \sim \text{Uniform}(a{\text{tier}},\, b_{\text{tier}}), \qquad \begin{cases} a_1=20,\ b_1=35 & \text{(Tier 1, sickest)} \ a_5=5,\ b_5=10 & \text{(Tier 5, least urgent)} \end{cases} $$
the departure rate naturally rose and fell with occupancy, exactly like a real ED.
- A "reactive" alert is already too late. Our recommendation engine originally only escalated once a Tier 1–2 patient had already missed their target wait. But because sicker patients are always served first (tier-priority placement), that trigger almost never fired — the system could sit at 100% bed occupancy with 38 patients waiting and still say "no action needed." We learned that a genuinely useful early-warning system has to project forward from a trend, not just threshold the present. We ended up computing a simple linear depletion rate from a short rolling window of bed availability,
$$ \text{rate} = \frac{b_{\text{old}} - b_{\text{now}}}{t_{\text{now}} - t_{\text{old}}}, \qquad t_{\text{to breach}} = \frac{b_{\text{now}}}{\text{rate}} $$
and started recommending action the moment $t_{\text{to breach}}$ fell inside a 10-minute window — turning "beds are full" from a lagging indicator into a leading one.
Determinism is a feature, not an accident. For the simulation to be trustworthy (and testable), the same seeded surge had to produce identical results every time, including in a second, invisible copy of the ED we ran in parallel purely to measure counterfactual impact ("what would have happened without WayPoint"). That meant re-seeding
random.seed()at every reset and making sure the shadow simulation never drew its own randomness — it only ever mirrored values the real simulation had already generated. Reproducibility in a stochastic system isn't free; it has to be designed in from the first line of the arrival generator.Clean" and "empty" are different goals. Midway through the project we did a full visual redesign, and the biggest lesson there was that removing "AI slop" — glows, pill-everything, chaotic micro-animation — isn't the same as removing personality. A single, disciplined accent color used only for calls-to-action, active states, and structural headers reads as more confident and more "real" than a dozen decorative flourishes.
How We Built It
Backend (FastAPI + SQLite): every patient, bed, nurse, and physician is a real row in a real table, not an aggregate counter. Placement, discharge, and turnover run inside BEGIN IMMEDIATE transactions so a surge tick and a dashboard poll can never race each other into an inconsistent state. A server-side surge controller (app/surge.py) generates arrivals on a ramp-up/peak/ramp-down profile with a hard tick-and-arrivals cap, replacing what was originally an unbounded client-side setInterval.
The shadow baseline (app/baseline_simulation.py): a second, fully in-memory copy of the ED that receives the exact same arrivals, tiers, and lengths of stay as the real one, but is architecturally incapable of using the float pool, overflow beds, or relocation. Comparing the two — using one shared, pure metrics function so the numbers can never disagree — is what lets the Impact tab say something like "reduced Tier 1–2 breach time by 74% and saved 2,300 patient-minutes of waiting," with a receipt behind every number.
The recommendation ladder (app/logic.py): a four-rung escalation — the ED's own turnover, the hospital float pool, overflow beds, then relocation — where every quantity is need-based (computed from actual load against realistic per-nurse/per-physician/per-room ratios) and capped at whatever the reserve actually has left, so it can never recommend more than physically exists.
Frontend (vanilla HTML/CSS/JS, no framework): a design system built from first principles — self-hosted IBM Plex type, an 8px spacing grid, and a token file (style.css's :root) that every component reads from. The dashboard evolved from a single scrolling page into a fixed-viewport application shell: a collapsible sidebar, a multi-panel "console" view for live operations, a sortable/filterable patient table, a split map/list view for the hospital network, and a slide-in detail drawer that reuses the same rendering code as the main view.
Challenges We Faced
The discharge bug that broke everything downstream. Beds pinned at 100% occupancy wasn't just a display bug — it silently broke recovery timing, the "last recovery time" metric, and made every capacity number on the dashboard meaningless. Diagnosing it required tracing the actual state machine transition-by-transition rather than trusting that "it looks like a discharge system."
A recommendation engine that was technically correct and practically useless. This was the hardest bug to see, because nothing was throwing an error — the ladder just quietly never escalated. It only became visible by instrumenting a seeded surge tick-by-tick and staring at 30 rows of "beds: 100%, rungs: [Monitor]" until the tier-priority interaction clicked.
Concurrency under load. A stress test with concurrent
Reset Demoand check-in requests surfaced a real race: a patient's row could be deleted by a reset between its own insert and its own read-back, crashing the request. Small window, real bug — the kind that only shows up when you deliberately try to break your own system.Rebranding a palette without breaking its own logic. When we moved from a teal accent to crimson red for the visual identity, red suddenly meant three different things in three different places — the brand accent, the "danger" status color, and a facility marker on the map. We had to redesign around that collision (e.g., giving the home-ED map marker and the "Stop Surge" button their own neutral charcoal treatment) so the meaning of red stayed unambiguous even as its frequency increased.
Chasing a UI bug that only existed for a fraction of a second. A reported "content overlap" turned out to be a missing
hiddenclass on two tabs — invisible after the first click (since the tab-switch handler re-hides everything), but present on every fresh page load. It's a good reminder that the most disruptive bugs are often the ones your own normal workflow never triggers.## Inspiration
Built With
- api
- canvas
- claude
- css
- css3
- fastapi
- git
- github
- grid
- html5
- ibm
- javascript
- leaflet.js
- mono
- openstreetmap
- plex
- pydantic
- python
- render
- rest
- sans
- sqlite
- unittest
- uvicorn
Log in or sign up for Devpost to join the conversation.