Project Info
By 2050, antibiotic resistance will have killed more people than cancer. Phagentic is the solution. Phagentic is a physical bioreactor with an operating system built to autonomously drive sensitive, hidden-state reactions. Its namesake - the bacteriophage - is biotech's most promising antibiotic alternative: a virus engineered to destroy otherwise-resistant bacterial strains. Because phages co-evolve with their targets, resistance never reoccurs But manufacturing is the bottleneck: it's a complex, multi-stage reaction that behaves differently every run. Phagentic uses a chemistry-informed model that continuously learns the reaction's kinetics while driving it. About Us We’re a team of three CS and one ECE student from Carnegie Mellon University. None of us had any previous biochem experience, but the ambitious nature of the challenge inspired us to give it a try. Learned wayyyy too much chemistry for this :) Trials and Tribulations The surface area of our project is massive: we were running a chemistry experiment, building the hardware for a bioreactor, writing the firmware, 3D printing part of the rig, creating the OS frontend/backend, and training the controller model all at once. We hit many challenges along the way, including: 3D printing challenges. Despite our print theoretically taking 2 hours, technical issues (and a quick visit from the fire marshal) led us to get our first working prototype at 2am. Along the way, we learned about tuning the slicer, print speed, and filament type to ensure that the print actually finished. Tuning the actual reaction. Before any controller could work, we had to make the reaction controllable. That meant real wet-chemistry iteration - adjusting the concentrations of glucose, NaOH, and dye to land the oscillation in a usable window: fast enough to demo, with a swing big enough for the sensor to read and slow enough for the controller to act on. Dozens of trial batches were spent dialing those ratios in. Moving from sim to real. We developed and tested the controllers in simulation against our ODE model first, where iteration is cheap — then moved them onto the live reactor, where they hit everything a sim doesn't have: sensor noise, time delay, reagent drift, BLE dropouts. Closing that sim-to-real gap was most of the real work. We also had some fun hack-y moments: Frying our first ESP32 when we were switching pins while it was plugged in (oops!) Almost melting the table because we forgot that NaOH releases heat when it dissolves Hand-whittling a stick with a pocketknife to hold the RGB corrector Doordashing duct tape when we needed waterproof adhesive at midnight Tech Stack The Chemistry The reactor runs the Blue Bottle reaction: a dye that flips between blue (oxidized, driven by stirring in oxygen) and clear (reduced by the glucose in solution). It oscillates blue → clear → blue and stalls once the glucose or base substrate runs out; a system that has to be fed and kept in rhythm. That's why it stands in for bacteriophage dynamics: a phage population fighting bacteria rises and crashes in the same predator-prey rhythm, and dosing it is the same closed-loop control problem. Blue intensity stands in for population, so the controller that holds the color on target is the one that would hold a phage treatment on target. A phage ⇄ blue converter in the UI makes the mapping explicit, as on a log scale, clear reads as zero phages and the deepest blue as a saturated culture of ~10¹². Hardware The hardware consists of an ESP32 chip connected to two pumps and the mixer through MOSFETs to control voltage. The RGB sensor plugs into the chip as well. Commands are streamed over a two-way Bluetooth Low Energy connection with the client laptop. Control Model The core of the project is a chemistry-informed ML model we trained to control the reaction. The model has four layers: State estimator. We clean the raw 20hz sensor data time series into meaningful signals about the true color, current pump states, and phage of the reaction. Gray-box ODE. Starting from a system of differential equations that model our target reaction, we fine-tune constants and dynamics on noised simulated and real-world run data, producing an accurate prior through which we can predict the future state of the reaction. Model-predictive control. The model searches through the action space and predicts the future using the learned ODEs for each action. It chooses the action that brings us closest to desired state. Continual learning. Each reaction is different, so as the reaction runs, we continue tuning the gray-box ODE predictors. The model is safety-gated by a classic PID loop; it isn’t allowed to take actions too drastic to prevent catastrophic failures. 3D Print We modeled and, using the provided 3d printers, printed a mount that caps onto the beaker for fixing the pumps' tubes, as well as a clip which slots into the mount for attaching the RGB sensor, and also provides a screen behind the fluid to diminish glare. This took a lot of troubleshooting and failed attempts with the different printers, but with some perseverance, we printed two iterations of our design, which, on the second try, met the tolerances and needs of our setup. Ethical Considerations Biochemical safety is critical, and we only trust scientists to handle critical decisions. Our core product ideology at Phagentic is that we're a tool to achieve what was earlier impossible in sensitive bio-manufacturing reactions, not a tool to replace scientists. As a result: The controller uses data from previous runs and a naive PID solver to gate possible actions to those deemed "safe." As a result, the model will never be allowed to drastically swing the reaction and cause a potential leak. The "Ask Phage" copilot is structurally read-only; it's designed as an analysis tool, and the safety-gated controller model is the only way to autonomously control the reaction. We incorporated easy fail-safes to quickly stop the reaction, and a manual mode for the scientist to take over driving if needed.
PHAGENTIC
A closed-loop controller for the Blue Bottle oscillating reaction. PHAGENTIC watches the live colour of the solution (blue ⇄ colorless), estimates the oscillation state — amplitude, period, phase, stall risk — and drives the stirrer and glucose pump to hold the rhythm.
The brains run headless in Python. A backend process owns the device link, the oscillation analysis, the control loop, and a pluggable ML model that drives the reaction. The web UI is a thin client: it renders the state the backend streams and sends commands back, over a single WebSocket. This means the control loop keeps running with no browser open, and you can drop in a real model (sklearn / torch / an RL policy) without touching the rest of the system.
phagentic/
├── backend/ # headless control backend ← the live system
│ ├── app.py # entrypoint: wires everything, serves uvicorn (python -m backend.app)
│ ├── server.py # FastAPI: /ws (state out + commands in) + GET /config
│ ├── hardware/ # device.py (DeviceWorker loop), roles.py, calibration.py, _hublink.py
│ ├── analysis/ # detector.py (oscillation extrema), signal.py
│ ├── control/ # model.py (Model interface), pi_model.py, arbiter.py, registry.py
│ ├── state/ # store.py (shared snapshot), commands.py, events.py
│ ├── protocol/ # messages.py (WebSocket message vocabulary)
│ ├── tools/ws_probe.py # headless WebSocket probe (verify without a browser)
│ └── tests/ # unit tests (no hardware needed)
├── frontend/ # the web UI — a thin WebSocket client
│ ├── index.html # built console (build.js assembles it from src/)
│ ├── api.js # WebSocket bridge to the backend
│ ├── logic.js # UI component (rendering + command sending; no analysis)
│ ├── runtime.js # vendored React-based template renderer
│ └── src/ # shell.html + widgets/*.html (built by build.js)
├── hub/ # DEPRECATED. Its device layer (controller/transport/config) is
│ # reused by backend/; dashboard.py + main.py are legacy tools.
├── controller/ # ESP32 firmware (generic pin API; pins configured at runtime)
├── experiment.md # the Blue Bottle experiment
└── Makefile # make backend / ui / setup / test / upload
Architecture
WebSocket protocol (ws://<host>:8080/ws)
Every frame is JSON {"type": ...}.
| Direction | Messages |
|---|---|
| server → client | state (full snapshot + narr_new[]), config (layout/roles/models), ack, calibration |
| client → server | set_actuator {role,value}, pulse_actuator {role,ms}, set_mode {mode}, set_model {name}, set_model_params {params}, recalibrate, reload_config, reset_run, ping |
role ∈ stirrer · light (PWM 0–255) and glucose · naoh (digital pumps).
Roles resolve to physical pins from hub/config.json by name match, so pins can
move in config without code changes.
What the UI sees and controls
- Sees: live solution colour (RGB swatch + lux), normalized blue intensity, oscillation waveform, amplitude, period/half-period, phase, cycle count, stall risk.
- Controls: Stirrer (PWM), Glucose pump (auto trigger + manual pulse, dose ms), NaOH pump (manual pulse), Sensor light (brightness), manual/auto/ml mode, model params, sensor recalibration, live config reload.
Run it locally
make setup # one-time: venv + deps (hub/.venv) for backend + hub
make backend # headless backend on http://localhost:8080 (ws://localhost:8080/ws)
make ui # web UI on http://localhost:5173 (UI_PORT=8000 to override)
Then open http://localhost:5173/. The UI connects to the backend over the
WebSocket and shows the hardware status in the header (⬡ HARDWARE when the
ESP32 is connected, ◌ NO DEVICE when the backend is up but the device
isn't, ◌ OFFLINE when the backend is unreachable — it auto-reconnects).
First load needs internet (the renderer pulls React from a CDN).
Hardware: power on the bioreactor (ESP32 flashed with controller/, advertising
as Bioreactor). The backend scans for it on start and re-pushes the pin map +
re-asserts outputs on every reconnect.
Verify without a browser
make test # unit tests (detector, PI model, arbiter)
python -m backend.tools.ws_probe # observe the live state stream
python -m backend.tools.ws_probe --mode auto
python -m backend.tools.ws_probe --set stirrer 200
URL params (frontend)
?backend=ws://host:8080/ws(orhttp://host:8080) — point the UI at a specific backend. Defaults tows://<page-host>:8080/ws.?view=console— skip the landing page and open the console directly.
Legacy: the hub/ dashboard
hub/ is deprecated as a UI but its device layer (controller.py, transport/,
config.py) is the reused, single source of truth for the wire protocol — the
backend imports it directly. The old wired tools still run if you need them:
make dashboard # legacy Flask dashboard (hub/dashboard.py, :8080)
make run # legacy terminal RGB stream (hub/main.py)
Configuration
hub/config.json is the single source of truth for wiring: MOSFETs
(name, pin, mode = pwm/digital) plus optional sensor_light. Edit it
and either restart the backend or send reload_config from the UI. Set
BLE_DEVICE to override the device name, BIOREACTOR_CONFIG to point at a
different config file, and PORT to change the backend port.
Analysis
View
Metric
- 17
- 17
- 6
- 5
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- AnthropicIn code
- FastAPIIn code
- FlaskIn code
- HTMLIn code
- JavaScriptIn code
- PythonIn code
- C++Claimed
- ReactClaimed
6 of 8 appear in the indexed code. 2 claimed on Devpost could not be matched to code, which may simply mean the tool leaves no trace in the repository.
AI coding agents
- Claude CodeConfig · Commits
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
871 KB
Source files
86
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
nirpechuk/phagentic
98 files · 28.3 MB · @ 2929903
Structure
Interface
19 files · 19%Screens, components and styles rendered to the user.
API & routing
1 file · 1%Request entry points: routes, handlers and controllers.
Application logic
53 files · 54%Domain rules, services and shared utilities.
+8 more
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- HTML53%
- Python20%
- JavaScript18%
- Markdown9%
- Shell0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
hub/requirements.txt
pypi · 4- anthropic
- bleak
- flask
- pyserial
backend/requirements.txt
pypi · 2- fastapi
- uvicorn[standard]
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
This project’s features have not been analysed yet.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.