← Projects

Content Studio

2026 · Kusha Sahu

What it is

Starithm tracks transient astronomy as it happens: gamma-ray bursts, gravitational waves, neutrinos, fast radio bursts. That makes a steady supply of things worth telling people about, and no time to write them up by hand. The content system is two small local tools that do most of that work, with a person still deciding what goes out.

content-studio generates posts. delivery reviews and publishes them. They never import each other. One writes a folder per post into a shared queue, and the other reads that folder. The contract between them is a single post.json file. Each repo keeps its own copy of the dataclass that describes it, and each copy carries a note to change both together.

One post, end to end. The folder in the middle is the only thing both sides share. Its status goes pending → publishing → published.

Generating: the model writes, code supplies the facts

Every format starts from a fact sheet, and where the facts come from depends on the format. Week in the sky and event story query the NovaTrace database directly, so counts, times and positions are never the model's to invent. Paper in slides picks a recent astro-ph.HE paper and gives Claude the whole PDF. Explainers start with a web-search research pass in which every fact keeps its source URL. Ears to the Universe takes a published sonification track and cuts a window out of it.

Claude then writes the copy against a Pydantic schema using structured outputs, so the shape is guaranteed and the prose is the only thing left to judge. The system prompt is short and strict: use only the facts provided, attribute and hedge single-team claims, no hype words, no em dashes, and write so that a curious fifteen-year-old can follow it and an astronomer finds nothing wrong.

After that the output is checked by code, not by asking a model whether it did well. Each paper slide has to carry a verbatim quote and a page number, and the quote is compared against the PDF text with ligatures, hyphenation and spacing normalised. Each explainer scene has to cite fact ids from the research sheet. The reviewer later sees those facts and links next to the post.

Rendering

Slides are HTML and Jinja templates. Headless Chrome screenshots them one at a time at Instagram's sizes, four in parallel. Narration comes from ElevenLabs with per-word timestamps. The response is cached by a hash of the text, voice and pronunciation dictionary, because credits cost real money and re-runs are common. A Pillow and ffmpeg compositor then builds the 1080×1920 reel. Each scene stays on screen exactly as long as its own words are spoken, captions highlight word by word, the image pushes in slowly, and the logo is stamped on every frame in a corner Instagram's own buttons don't cover. One small detail: the voice says “Starithm dot A I” and the caption shows starithm.ai.

Paper reel on Instagram ↗
mm-jets, arXiv:2610.20741. Six Manim scenes, all Claude.
Guess the object on Instagram ↗
Ears to the Universe. Answer options in the caption.

Illustrations a model codes and code checks

Paper and explainer reels get one animated Manim illustration per narrated sentence. A model writes each scene as Python against a small house kit: a label helper, the brand colours, one fixed colour per messenger (gamma rays yellow, neutrinos green, gravitational waves violet), and a base class that times every animation as a fraction of the sentence's length. Code written by a model from untrusted paper text is not something to just run, so every scene has to pass four gates.

The four gates. Failures go back to the model as plain-language problems, up to two fixes, before the other model tries.
  1. Static. The AST may import only the kit, Manim, numpy and math. No open, eval, getattr, no dunder attributes, nothing that touches files, the network or the system.
  2. Render. The scene runs in its own process with a 240-second timeout, so a broken scene can't hang the pipeline.
  3. Layout. After the last frame, the base class checks that no two labels overlap and that nothing sits outside the frame, and writes a report.
  4. Facts. Every number on screen has to appear in that scene's narration, text or quote, including numbers spoken as words (“twelve point six”). Every acronym has to appear there too, so an invented instrument name fails.

Claude writes the scenes while a credit grant covers it, then Kimi K2.7-Code on Featherless takes over as the main model. The switch is a date in the config, and whichever model is not the main one is the fallback. A scene that fails both models falls back to the stock diagram the script picked for it. There are seven, among them an inspiral, ripples, a jet, a signal path and a timeline, each animated in Pillow in the same palette. So a reel always ships, and it still looks like the others. Passing scene code is saved next to the reel, so redrawing it after a narration or kit change costs no AI calls.

Choosing the models was its own experiment. Claude, Qwen3-Coder, two Kimi versions and a hybrid (Claude storyboards, a cheaper model writes the code) each illustrated the same paper reel over the same narration. Every version was built into a full reel and watched next to a hand-written one, with gate passes, fix attempts, tokens and time recorded per scene.

Delivering: a person in the loop

delivery is a small Flask app on 127.0.0.1. The list page polls a version stamp built from the queue's file count and newest modification time, so a post appears about five seconds after content-studio writes it, without a refresh. post.json is written to a temp file and then renamed, so the reader never sees half a file.

The delivery review UI's queue page, titled Content Studio delivery, with Queue and Analytics tabs. Ten pending posts are listed, each with a thumbnail, its format and kind (Explainer · reel, Paper in slides · carousel · 7, Event story · carousel · 6), a title such as SN 1987A Neutrinos or New paper · arXiv:2610.27312, and the date it was made.
The queue. Pending, published and rejected posts, with format, kind and slide count.

Each post opens with its media, an editable caption and first comment, and the fact sheet the copy was written from, with a source link on every fact.

A post open in the review UI: the reel's first frame on the left (A star exploded. The evidence came first.), and on the right the editable caption about SN 1987A, the first comment listing sources, a character and hashtag count with a Save caption & comment button, and the start of the fact sheet with 9 facts used, each linked to its source.
Reviewing an episode of The Universe Left Receipts: caption, first comment and the sourced fact sheet.

Further down are the reviewer notes (why the post was picked, caveats, which model drew which scene) and what the run cost in Claude tokens, web searches and ElevenLabs characters. From there you can reject the post or post it.

The bottom of a post in the review UI. Cost of this run: 161,606 Claude tokens in, 29,431 Claude tokens out, 5 web searches, 493 ElevenLabs characters, over 9 calls. Notes: Case 03 of 8, 35 seconds, 17 sourced facts researched and 9 used, synthetic voice labelled AI-generated, all five illustrations drawn by Claude. Then Alt text, Narration script and Sources, and the Post now to Instagram and Reject buttons.
What the run cost, the notes for the reviewer, and the decision.

Posting uploads the media to a private Cloudflare R2 bucket and gives Instagram signed links that expire after an hour, so nothing is public before it is published. It then creates the Instagram containers (one per carousel slide), waits for each one to finish processing, publishes, and adds a first comment with the event, paper or blog link, since captions can't carry clickable links. If the comment fails, that is reported but doesn't undo the post. Reels made with a synthetic voice are flagged as AI-generated.

While the server runs it pulls account and per-post insights every six hours and appends a snapshot to a JSONL file, so the history builds up over time: views in the last 24 hours, average watch time as a share of the reel's length, and sends and saves per reach. The same loop renews the 60-day Instagram token once it is 30 days old.

Numbers

As of 23 October 2026.

  • Seven formats: week in the sky, event story, Ears track, guess the object, explainer, paper in slides, and an eight-episode reel series, The Universe Left Receipts
  • 15 posts generated into the queue so far, 5 published
  • A narrated explainer reel takes 9–10 Claude calls and 160k–460k input tokens, most of it web research. A paper reel took 6 calls and 13k input tokens.
  • Early reach: 1,012 views across the account in the last 7 days. The kilonova explainer reel had the most views of any pipeline post, 296, with an average watch of 10.4 s out of 48.6 s. Holding attention past the first few seconds is the open problem.

The posts go out on Instagram as @starithm.ai, and the events they describe are live at starithm.ai. NovaTrace, which most of the facts come from, has its own write-up.