Data acquisition
Querying IRSA's public archive for ZTF frames of one field, downloading them, and caching the login instead of re-authenticating on every run. Unglamorous, and every later stage depends on it working quietly.
This page opens fine from the filesystem, but the viewer fetches ~144 MB of FITS and
catalogue files — and browsers block fetch() over file://.
Serve the project once and both pages work:
./serve.shThen open http://localhost:8765/showcase/
There is too much sky for anyone to watch by eye — trillions of stars, and the objects worth catching are the ones that move, flare, or arrive and are gone in a night. This project is an AI-based change-detection pipeline built to notice that automatically: feed it two images of the same patch of sky taken at different times, and it aligns them, subtracts the static sky, detects what's left, and tries to say honestly what it is — a known asteroid, a variable star, an active galaxy, an artifact, or something it can't yet be sure about. Everything below is that pipeline and how it got built.
The Overview page, the eighteen-tube instrument, Cerro Tácana, the alert-latency numbers — that's an invented observatory, built as a web-design demonstration wrapped around this project's real technical spine. This page is the exception. It describes the actual project: a solo build, done during a PVNet internship, using real ZTF survey data and a real pipeline. Nothing below is fiction.
Training a real-time astronomical classifier from scratch costs compute and a labeled dataset a solo project doesn't have. So the realistic goal here was never "discover something new" — it was to take existing, published, pretrained systems (braai, ALeRCE's classifiers, DeepStreaks) and existing image-subtraction methods (ois, ZOGY), wire them into one working pipeline end to end, validate it against ZTF's own data, and put the results behind a public interface nobody else had built for this combination of tools. That's what this project is. It has made zero novel discoveries — every result the viewer, survey browser, and cell stacks show is a recovery of a known object, an unconfirmed candidate, or a classifier verdict, never a discovery.
A change-detection pipeline does not find things by looking harder. It finds things by removing everything it already knows, and being disciplined about what it does with the remainder.
Every star in a ZTF exposure sat in almost exactly the same pixels the last time this field was imaged. This pipeline is not interested in any of them.
A near-Earth asteroid on approach is visible for a handful of nights and then not again for decades. A supernova declares itself over about six hours. If nobody happens to be pointing at that patch of sky in that window, the event simply did not happen as far as the record is concerned.
Deep single-target telescopes are better than us at everything except being in the right place. Our advantage is that we are in every place, every four minutes.
Difference imaging aligns each new frame to a deep reference stack, matches the point spread function, scales the photometry and subtracts. What survives is change: something brightened, something faded, something arrived, something moved.
It never subtracts perfectly. Registration error leaves dipoles, saturated stars leave craters, and a bad night leaves the whole frame gently wrong. Handling that honestly is most of the engineering.
Detection runs at three sigma, which over-detects on correlated noise, on purpose. A false candidate costs a few milliseconds of classifier time. A real fast mover thrown away at the detection stage is gone — there is no second exposure of that minute.
So the detector is generous and the classifiers are strict, in that order. Reversing it would look tidier in the logs and would be wrong.
This project doesn't operate an observatory — it reads from one that already runs every clear night: the Zwicky Transient Facility, on Palomar Mountain outside San Diego.
ZTF images the sky with the 48-inch Samuel Oschin Schmidt Telescope and serves its public archive through IRSA at NASA/IPAC — free, no telescope time to win, just an account and a query. That's what made a solo pipeline like this possible at all: the imaging problem was already solved by a professional survey; this project's job was everything after the shutter closes.
Almost everything here works one field at a time — RA ≈150°, Dec ≈2°, inside the COSMOS survey footprint, imaged roughly twice a night. That single field carries the whole pipeline: alignment, subtraction, detection, classification, motion-linking, all proven on it before the survey harvester generalized the same code to any field ZTF has imaged.
The honest caveat: ZTF's own difference images come from a deep, professionally-built reference co-add. This project's homemade reference — stacked from public archive frames — is shallower, and that gap turned out to be the single biggest limit on how well this pipeline can judge its own detections. It's documented, not hidden, below.
Nothing here was built as one finished system. Each stage had to work and be validated against real data before the next one existed — that's what made it possible to catch mistakes early instead of discovering them at the end.
This is solo work, not a funded build — the "log" below is really six technical problems, solved and validated in order, each one covered in far more depth in the project's own running build notes. Dates are approximate where the exact day isn't on record.
Construction log component did not load.
This was solo work, not a team — but six genuinely different technical problems had to be solved, in order, before the next one made any sense. These are the areas, not a staff list; the tools named are the ones actually in the codebase.
Querying IRSA's public archive for ZTF frames of one field, downloading them, and caching the login instead of re-authenticating on every run. Unglamorous, and every later stage depends on it working quietly.
Reprojecting every epoch onto one shared WCS pixel grid so a fixed star lands on the identical pixel every time, then median-stacking dozens of the sharpest epochs into a deep reference template to subtract against.
Subtracting reference from science with PSF and photometric matching, then checking the result against ZTF's own official difference image for the same exposure — the reality check that caught most of the mistakes below.
A pretrained CNN (braai) gates real from bogus; ALeRCE's stamp and light-curve classifiers, plus a locally-run backup CNN, decide what survives — variable star, active galaxy, supernova, or asteroid.
A second, parallel cascade (DeepStreaks) flags streaks and separates fast asteroids from satellites; a custom linker fuses same-night detections across fields into one track with a measured rate — this is how the real asteroid 2019 BE5 was recovered blind, with no ephemeris fed into detection.
Every result — recoveries, candidates, verdicts, and what got rejected — lands in a results store, exports to static JSON, and renders in the difference viewer, survey browser, and cell stacks linked from the nav above.
These are operating rules this pipeline was actually held to, not aspirations. Each one shows up somewhere in the code or the results, which is how you can tell it's real.
Every candidate the classification cascade throws out is still recorded and shown — the survey browser's "bogus" and "unconfirmed" categories aren't hidden; they're the majority of what the pipeline finds. A system that only shows its best guesses isn't one you can check.
Every result is labelled as exactly one of three things: a recovery of a known object, an unconfirmed candidate, or a classifier verdict — never a discovery. This project doesn't have the telescope time or the resources to find something genuinely new, and saying otherwise would be the one truly dishonest thing a page like this could do.
A position without an uncertainty is a rumour. Rates, position angles, and classifier scores are reported with the measurement that produced them, and a lookup that couldn't complete is labelled "unchecked" — never quietly folded into "no."
Results are only as durable as the code that made them. A regression test locks the exact detection catalog for one known field so a number produced early in the build still reproduces bit-for-bit today.
Detection runs deliberately loose — the pipeline would rather hand the classifiers a thousand artifacts than lose one genuine faint or fast object to a tidy threshold. An explicit, unglamorous trade, made every time a threshold in this project gets touched.
Open source, no sign-up. Everything on this page is the real project. NOCTIS ARRAY, the invented observatory on the Overview page, is a design skin built as a demonstration — but the difference viewer, survey browser, and cell stacks it links to render genuine public ZTF data through this actual pipeline, with no discovery claimed anywhere.
Everything above is the real project. The links below are its working output — a ZTF difference-imaging pipeline rendering real observations, as a survey across many fields or one patch of sky at a time.
Or read how it works, stage by stage