Quick facts
- Cadence: one broadcast a day, seven stories, about three and a half minutes of show; a teaser on YouTube about ninety minutes later.
- Hardware: one Mac (an M1 Max that does nothing else in the morning) plus the website's own server, which does the scraping.
- Models: gemma4 26B for editing and script writing, qwen3 1.7B for article summaries, Chatterbox for the voices, faster-whisper to check the voices. All run locally, all open weights.
- Cost: electricity. No language-model, voice or video API bills; the YouTube Data API is free within its daily quota.
- Run time: 17 to 22 minutes from the 9:00 cron to a ready show over the past week, then five to ten minutes for the recording and upload at 10:30.
- Humans in the loop: none on a normal day. The checks are automated, the failures email a person, and a person can regenerate a day.
The pipeline at a glance
Two machines and one day. The website's server gathers the news early each morning and keeps it in a table. At 9:00 Eastern a cron job on the Mac turns that table into a show: it groups articles that report the same event, summarises each one, picks seven stories, writes a script for two anchors, voices every line, checks every line, measures every line, and publishes the result to /news/show, where the studio plays it in the browser. At 10:30 a second job records that studio, cuts a teaser and uploads it to YouTube. Nothing in the chain calls a paid API.
1. Scrape: where the news comes from
The scrape is the only part of the pipeline that runs on the website's server rather than the Mac. Once a day a Laravel console command asks four kinds of source for anything new: Google News RSS for six queries (UFO, UAP, alien life, extraterrestrial, SETI signal, UFO sighting); the Hacker News search API for four terms, keeping items with at least three points from the last seven days; the Atom feeds of four subreddits (r/UFOs, r/aliens, r/HighStrangeness and r/UFOB); and 38 publisher RSS feeds. Nine of those feeds are on-topic by nature (The Debrief, Open Minds, The Black Vault, Avi Loeb's Medium, Centauri Dreams, two Phys.org feeds, Universe Today, Sky & Telescope) and skip the keyword test, because an astrobiology paper belongs on the show even when its headline never says "alien". The other 29 are mainstream science and world feeds from the BBC, CBS, NBC, ABC, NPR, The Guardian, The New York Times, The Washington Post, Al Jazeera, Time and others, and an item from them is kept only if its title or description matches one of a short list of patterns: a UFO or UAP, an alien, extraterrestrial, SETI, ufology, or the phrase "unidentified anomalous phenomena" spelled out.
The exclusion list is as important as the inclusion list. "Alien" in the news usually means immigration, so anything about visas, deportation, border patrol or the Alien Enemies Act is dropped, and so is the film franchise: Xenomorphs, Alien: Romulus and Sigourney Weaver never make it in. Articles are kept for 90 days and then pruned. Publisher feeds matter for a reason that is not obvious: a Google News link cannot be followed to the article (it bounces once and then hands off in JavaScript), so a story that arrives only through Google News can be summarised from its headline alone. A story that arrives through the publisher's own feed can be read in full.
2. Cluster: one story, many outlets
With that many feeds, one event routinely arrives as four articles from four outlets, and a broadcast that read all four would sound like it had one story. The first thing the Mac does at 9:00 is hand the morning's candidate pool to the large model with a wire editor's instructions: group the items that report the same underlying event, best version first. The grouping is cleaned up in code (ids that do not exist are dropped, an id can sit in only one group, a group of one is not a group) and pushed back to the server, which collapses the /news feed to one lead per story with "Also covered by" chips for the rest.
The same grouping shapes the show. The pool handed to the editor is collapsed to one representative per story, so the same event can no longer be picked twice, and each representative knows which other outlets are on it, so the anchors can say so on air: a story that the BBC and Time are both reporting is usually a bigger story, and the script rules ask the reader to name them. If the model call fails, the pool is simply left unclustered, which is exactly what the pipeline did before clustering existed.

3. Summaries: three neutral sentences per article
Every article that needs a summary gets one, whether or not it makes the show, because the summaries are what the site shows on the article pages and what the script writer works from. The summariser fetches the URL, runs Mozilla's Readability to pull the article body out of the page, and asks a small local model, qwen3 at 1.7 billion parameters, for three neutral sentences. Small is the point: turning an article into three sentences is an easy task, and on a machine that has to share its memory with a 17 GB model later in the run, the difference between a small and a large summariser is the difference between seconds and minutes per article. When the pipeline was measured, the 26B model took more than two minutes for one summary while the Mac was swapping; the 1.7B model produced a clean three-sentence summary in seventeen seconds including its own load time.
Headline-only items, the ones that arrived through Google News, get a deliberately shorter summary and an instruction not to pad, after an early version was caught inventing detail because it had been told to keep writing. On the site every summary is labelled as AI-generated with a link to the source, and the morning's batch of 24 to 50 articles takes between 1.2 and 1.8 minutes.

4. The script: an editor and a writer, both the same model
Two model calls turn the pool into a show. The first is the editor. It sees a numbered menu of the day's stories with their outlets and their "also covered by" notes and is asked to pick the seven most interesting, favouring real scientific findings, official disclosure news, credible sightings with actual detail and genuinely surprising items, and avoiding local club meetings, vague "any ideas?" photo posts and things that merely matched a keyword. It is also asked to spread the sources, and that rule is enforced in code rather than trusted: no outlet may contribute more than two stories, because the same model ignored a mandatory rule about source credits two thirds of the time when it was only asked nicely. Ids the model invents are dropped, and if the call fails altogether the seven most recent stories air.
The second call writes the script as structured JSON, constrained by a grammar so the output cannot be malformed. The format is reader and reactor: one anchor reads a story, the other gives a short reaction, and they swap every story. The rules the writer is given are the editorial policy of the show. Facts come only from the provided summaries and nothing may be invented; the reporting must name concrete details (who, which instrument, what number, what it rules in or out); every story must credit its outlet out loud, in varied phrasing; a story other outlets are covering must say so; the first story opens with an opening segue and later ones with a continuing one; at most 56 lines; no stage directions. The two anchors have written personas, Zyx warm and delighted by humans, Aria curious and equally a reporter, and the writer is told they like each other.
Then the code checks the writer's work. A story whose reporting never names its outlet gets a credit injected deterministically; a story the writer skipped triggers a re-roll, up to three times; a second segment tagged with an article the script already used is caught by the duplicate-segment check; a script that starts mid-broadcast has its first segue rewritten. This is the pattern throughout the pipeline: ask the model for what you want, then verify in code what you cannot afford to get wrong.
From the September 5, 2026 script
Zyx (4.5 s): I was just telling Aria how much I adore the way humans study their own history.
Aria (4.6 s): It's quite endearing, Zyx. I'm Aria, by the way, for those just tuning in.
Zyx (2.8 s): And I'm Zyx! Let us get to the news.
Zyx (7.8 s): Leading off tonight, journalist Tom O'Neill reports that a Congressional Staffer claims the US possesses NHI craft and bodies.
Zyx (8.4 s): According to the UFO subreddit, the information within the SCIF is described as terrifying and psychologically disturbing.
Aria (3.7 s): Oh dear, that sounds terribly heavy for them to carry alone.
Twenty-nine lines, seven stories, 186 seconds of speech. The durations come from step 6.
5. Voices: local speech synthesis that listens to itself
The voices are Chatterbox, Resemble AI's open-source text-to-speech model, running on the Mac's GPU behind a small local server. Each anchor's voice is cloned from a reference clip of six to ten seconds, and the clips themselves were made with the macOS say command, so the whole voice chain is reproducible from a shell script. Every line of the script is synthesised separately, converted to MP3 with ffmpeg and uploaded with the show. Because the 17 GB language model and the speech model do not fit in memory together, the pipeline stops the speech server before the language model runs and starts it again after; voicing the lines is most of the fifteen to nineteen minutes a show takes.
The check exists because of one bad morning. On August 22, 2026 the model voiced a perfectly ordinary line as twelve seconds of spelled-out letters, and it aired. Since then the speech server listens to what it made before handing it back. Each candidate is transcribed by a small Whisper model (faster-whisper, base.en, on the CPU, because the GPU is busy) and compared with the script by token similarity, and its speech rate is checked against a window of eleven to twenty-six characters per second: a model that is spelling runs slow, and a model that skipped text runs fast. A candidate that fails either test is re-synthesised with a cooler sampler, up to three attempts, and the best one is returned with its verdict in the response headers; the run log lists any line that aired unverified. The thresholds were calibrated on 189 real lines, and the gate flagged exactly the broken one.
6. Mouths: a loudness envelope, twenty times a second
The anchors do not lip-sync in the phonetic sense; they open their mouths in proportion to how loud the voice is, which for a cartoon alien is convincing. For every line, ffmpeg decodes the MP3 to mono 8 kHz PCM, the pipeline takes the root-mean-square loudness of each 50-millisecond window, normalises the result to integers from 0 to 100, and stores that envelope with the line: twenty numbers per second, 89 of them for the 4.45-second opening line shown below, computed from the actual audio file of that line.
Getting a mouth to open at all was the harder problem. The anchor models came from a generative 3D service with body rigs and baked animations but no facial bones and no blendshapes, so a small tool in the repository synthesises a jaw-open shape key programmatically: it selects the head vertices by their skin weights, orients them with the rig's head marker bones (the head bone's own axes turn out to be tilted about 40 degrees off the anatomy, which was the first silent bug), rotates the lower muzzle down around a hinge, and writes the displacement as a morph target named mouthOpen. No Blender is involved. In the browser, the player reads the envelope at the audio's current position and drives that morph target with a little attack and decay; one anchor's face flaps more subtly than the other's, so each has a gain.
7. The studio: three.js, two GLB models and NASA on the back wall
The show is not a video on the site. It is a three.js scene, built with React Three Fiber, that plays the script live in your browser: the two anchor models with their baked animations (Zyx sways, Aria gestures; both are slowed almost to stillness while the other is speaking), a desk, a chyron with the story's category and a counter, a caption of the line being spoken, a ticker of the day's headlines, and a back wall showing a public-domain NASA image that changes with every story. A "coming up" rundown counts down for eight seconds when the page loads, the audio for the whole show is prefetched so a slow connection does not stall a line, and between stories there is a three-second beat with a headline card and a short synthesised sting. Each day's show has a permanent address at /news/show/{date} and stays in the archive for two weeks.
Publishing the show is the last thing the 9:00 job does. The MP3s and envelopes go to object storage, the script goes to the server, the show is marked ready, and subscribers get a push notification and an email. A server-side check a couple of hours later alerts a human if no ready show exists for the day, which covers the failures the Mac cannot report itself, such as being asleep.

8. Recording it: the capture that cron kept ruining
At 10:30 the second job loads the show in a headless Chromium under Playwright at 1920 by 1080 with a flag that gives it the real GPU, and records it. The page has a capture mode: it starts on a signal from the recorder, keeps a black cover over the scene until the first line begins, and logs the moment each line actually starts. ffmpeg finds the end of the black cover on the recording's timeline and uses it to anchor those offsets, and because playback inside the recording is muted, the audio track is rebuilt from the real per-line files at the captured positions. A long black lead is free, so the browser is allowed to settle, faulting in its working set and compiling the studio's shaders behind the cover rather than mid-show.
For two weeks the recordings came back roughly twice as long as the script, all of it dead air between lines, and the first two explanations were wrong. Memory pressure from the 9:00 run was real once, so the job moved to its own slot and gained a settle wait; display sleep was blamed next, and keeping the display awake demonstrably worked and was not enough. The cause was the execution context. On macOS a job started by cron, or by a launchd agent without a process type, runs with throttled timers and I/O, and the show player's waits between lines stretched into recorded silence. The same show, on an idle machine, measured like this:
| How the recorder was launched | Recorded | Script | Ratio |
|---|---|---|---|
| cron | 392.0 s | 212.3 s | 1.85× |
| launchd agent, no ProcessType | 356.4 s | 212.3 s | 1.68× |
| launchd agent, ProcessType=Interactive | 214.0 s | 212.3 s | 1.01× |
| a person's shell | 214.0 s | 212.3 s | 1.01× |
So the recorder now runs as a launchd agent with ProcessType set to Interactive, inside a window in which the Mac is kept awake, and the guards stay as insurance: the job waits for memory pressure to clear before launching, waits again inside the browser for up to fifteen minutes, and compares the recording's length with the script's own arithmetic (every line's duration plus the real between-story beats). A recording more than 1.35 times the expected length plus fifteen seconds is thrown away and captured once more after a pause; a second overrun fails the run loudly rather than uploading dead air. Since the fix, recordings land within about two seconds of the script.

9. Cut, outro, thumbnail, upload
The YouTube video is a teaser, on purpose: the last story is dropped when there are at least three, so that the full broadcast, with every source link, stays on the site. ffmpeg cuts the recording on a story boundary, muxes the rebuilt audio, and appends a six-second outro card that points at alientavern.com/news. A thumbnail is rendered at 1280 by 720 from a real studio frame with the day's top headline and a one-line quip written by the model during the morning run, and the video's title and description are built from the script: the top headline, a link for each story, and a link to the full broadcast.
The upload goes through the YouTube Data API as a resumable insert and costs about 1,600 of the project's 10,000 daily quota units, which is fine for one show a day. Before any of that, the job claims the day: it asks the server which broadcast still needs a video and marks it taken, because an upload is not something you can safely do twice. If a run fails after claiming, the claim is deliberately left in place and the log prints the exact command to clear it once a human has checked the channel; clearing it blindly could produce a duplicate. Two honest footnotes: until Google audited the API project, every upload was locked private regardless of what the pipeline asked for, and the channel has been public only since early September 2026; and YouTube currently refuses the custom thumbnail with a permissions error, so the videos run with the frame YouTube picks until that is sorted out.


10. Links back to the Knowledge Base
The last step is the one that makes the news useful after the day it aired. Every article in the site's Knowledge Base carries a short list of topic phrases, and the server matches them against headlines and summaries, whole-phrase and case-insensitively, so that an article page can show "Background from the Knowledge Base" (the three best-matching explainers) and a Knowledge Base article can show "Latest news on this topic". Both are computed on the server and rendered into the HTML, so the links exist for crawlers as well as readers. A story about a Navy pilot links to the case file on the Tic Tac; a story about a new exoplanet links to the habitable-worlds explainer; a story about a Grey in a Netflix series links to the field guide of fictional species.
Honest numbers
These are the figures from the run logs for the four most recent complete runs, not estimates.
| Phase (9:00 job) | Time | Notes |
|---|---|---|
| Summaries | 1.2–1.8 min | 24 to 50 articles a morning, 1.7B model |
| Daily tidbits | 0.7–1.0 min | the /daily page; one structured call, non-fatal if it fails |
| Script, voices, checks, publish | 15.6–19.1 min | seven stories, about thirty lines, three tries per line if needed |
| Total | 17.5–21.5 min | cron start to a ready show |
- The September 5, 2026 show: 29 lines, 7 stories, 186 seconds of speech, about 3 minutes 30 seconds with the beats between stories.
- Recording: within about two seconds of the script's arithmetic when launched correctly; 1.68 to 1.85 times too long when it was not (see step 8).
- YouTube: one upload costs about 1,600 of 10,000 daily quota units. The teaser keeps six of seven stories.
- Cost: the Mac was already owned and the server already runs the site. The models are open weights (Gemma, Qwen, Chatterbox, Whisper). There are no per-call bills anywhere in the chain.
- What it is not: fast. A cloud API would write and voice the show in a fraction of the time. Running it locally is a choice about cost and control, and the run has a whole morning to itself.
What fails, and how it recovers
Most of the engineering in this pipeline is recovery, added one failure at a time.
- A voice line comes out garbled. The read-back check in step 5 rejects it and re-synthesises, up to three times; a line that still fails is logged as unverified rather than silently aired.
- The writer breaks a rule. Missing outlet credits are injected in code, skipped stories force a re-roll, duplicated segments are caught, and a wrong first segue is rewritten. The model's output is treated as a draft.
- The editor picks badly. Invented ids are dropped, the two-per-outlet cap is enforced, and if the call fails the seven most recent stories air. A show with a weaker rundown beats no show.
- The tidbits fail. The /daily page is generated in the same run and is non-fatal: the show continues without it and the failure is reported.
- cron itself. cron's PATH is bare,
npxhung indefinitely under it (a run once sat wedged for three hours), and Node buffers its output when it is writing to a file, so a failed run left an empty log. The run is now a wrapper script that sets its own PATH, calls the TypeScript runner directly, logs unconditionally with start and end timestamps, and checks its preconditions before starting. - The speech server wedges. A stuck Chatterbox never heals itself while still answering its health check, so the server is started fresh for every run and stopped after.
- The recording comes back long. The dead-air guard in step 8: retry once after a pause, then fail loudly with the claim left in place and the recovery command printed.
- The Mac never ran at all. The wrapper emails on a failed run, but it cannot email about a run that did not happen (laptop asleep, no network), so the server runs a dead-man check a couple of hours after the slot and alerts if no ready show exists for the day. That check was added after a missing show was noticed by hand.
- Someone has to know. Failed runs send an email and, where it applies, a Sentry event, with the failing phase named. The August 22 letter-salad line was reported by a listener; everything since has been reported by the pipeline.
What the AI does, and does not do
The honest description is that the models read, summarise, choose and perform; they do not report.
- Every story is real reporting by a named outlet. The anchors read summaries of articles that exist, credit the outlet out loud, and the site links to the source on every story. The script writer is given only the summaries and the instruction not to invent events; the credit rule is then enforced in code.
- The reactions are opinions, and they are the characters'. Aria's "how sweet" and Zyx's delight in humans are written to the anchors' personas. They are the part of the show that is fiction, and they are the only part.
- The summaries are labelled. Every AI summary on the site says so and points to the article. Headline-only items are kept short on purpose.
- The editorial rules are written down and mechanical. No outlet gets more than two stories a day; local event listings and vague photo posts are avoided; immigration stories and the film franchise never enter the pool; a story several outlets are covering is weighted up and the other outlets are named.
- Nobody edits the script before it airs. That is worth saying plainly. The checks in steps 4 and 5 are automated, a person reads the logs, and a bad day can be regenerated. The show is a machine reading the news, not a newsroom pretending to be one.
- The one place the model writes original material is the daily tidbits: a joke, a poem, a short scientist biography and a maths fact, labelled as generated, on their own page.
- No generated images or video. The studio is a real 3D scene, the backdrops are NASA photographs in the public domain, and the anchors are the same models that walk around the site's 3D lobby, with a mouth added.
Where to watch, read and subscribe
- Today's broadcast, in the browser, with a two-week archive of past shows.
- The YouTube channel, where the daily teaser lands about ninety minutes after the show.
- The news feed, with every source, and its RSS feed for news readers. The daily show can also arrive by email: the sign-up is at the top of the feed.
- The daily tidbits and the Knowledge Base, where every story's background lives.
- The podcast feed: the same broadcast as audio, one episode a day, mixed from the anchors' lines right after the show airs, for any podcast app.
The code for all of this is in the Alien Tavern repository: the summariser and show generator, the uploader, the mouth-morph tool and the studio components. Questions from journalists, podcasters and engineers are welcome through the contact page.
Join the Alien Tavern Community
Connect with fellow enthusiasts, share your own sightings and theories, and stay updated on the latest in UFO and alien research.
Sign Up Free