Inspiration

When I was in my early 20s, the War in Iraq happened. Since childhood, I have always hated war and wondered why grownups and nations couldn't just talk to each other. People were forming strong opinions based on limited information, and on early social media (ICQ) I ended up connected to a man in Iraq during the conflict. I was sitting eating breakfast, and I could hear gunshots in the background while he calmly described his day — "It's not so bad, during the day you can go get groceries, but at night you stay in." That conversation stayed with me. I had come up with the idea for Newspectives back then in my naive hope to diffuse people's viewpoints a little. I tried it with student collaborators and the technology wasn't there. The idea sat dormant for two decades.

Then Gemini 3 Flash with Google Search grounding landed, and suddenly the missing piece — credible, real-time, multi-source synthesis at scale — was a single API call away. Newspectives became buildable.

What it does

For every news event, Newspectives runs a configurable roster of AI agents in parallel — USA, China, Russia, India, Germany, the UK, Israel, Arab World, Latin America, South Africa, Humanitarian, a satirical "Jester" lens, and a Common Ground synthesis — each grounded in that region's actual press (BBC, Global Times, RT, Al Jazeera, NDTV, Times of Israel, etc.). The output is side-by-side: same event, every regional frame, with tone classification, citations, and an editor-curated Common Ground that surfaces what's actually agreed on.

It also generates a daily 8-minute AI radio broadcast (Gemini Native Audio), an original song per perspective (Suno), and a 20-second portrait video per topic for social distribution (HyperFrames). All open API, all transparently AI-generated, all human-overseen.

How we built it

Stack: React 19 + TypeScript + Vite SPA, Express on Firebase App Hosting (Cloud Run under the hood), Firestore for persistence, Firebase Storage for media.

The Google AI layer is the moonshot itself:

  • Gemini 3 Flash with Google Search grounding for the per-region perspective analysis — 13+ parallel agent calls per topic, each with a region-specific system prompt that deliberately includes state-influenced outlets so readers see actual regional framing, not a filtered consensus.
  • Imagen 4 for topic illustrations.
  • Gemini Native Audio for the daily radio broadcast and per-topic audio summaries.
  • Google Search grounding as the citation backbone — every claim resolves to a real article from the relevant timeframe, not training data.

The GEO layer (Generative Engine Optimization — the discipline of making content citable by AI search) became its own product surface. Every topic page ships a NewsArticle schema whose hasPart is an ItemList of Claim items, each with a per-perspective @id anchor and a citation array. The Jester carries a disambiguatingDescription marking it as satire so downstream models won't hallucinate it as fact. We publish llms.txt, llms-full.txt (42K words of bulk context), ai-plugin.json, and an OpenAPI spec — so ChatGPT, Claude, Perplexity, Gemini, and Bing Copilot can all consume the platform directly.

Challenges we ran into

Firebase App Hosting silently bypasses firebase.json's hosting block. This is documented nowhere prominent. We migrated from classic Hosting in late 2025 and our sitemap.xml, sitemap-news.xml, podcast RSS, and music RSS feeds just… stopped serving. No errors, no warnings, just 404s. App Hosting routes every request to Express, and the redirects we'd declared in firebase.json were ignored. Fix: explicit Express proxies in server.ts that fetch the XML from Firebase Storage. Lesson worth more than the bug — infrastructure migrations can void config you forgot was load-bearing.

The Suno music silent failure. A music generation request could hang on "GENERATING AUDIO…" for the full 8-minute polling window, then time out, with zero record of the job in the Suno dashboard. Root cause: when Suno rejected the request (rate limit, banned style word, malformed payload), data was null, taskId became undefined, and JSON.stringify silently dropped the field from our response. The client then polled /api/music/status/undefined for 50 attempts, and Suno cheerfully responded {code:200, msg:"success", data:null} for the unknown task ID. The fix touched three layers — server-side validation that returns a real 502 with the Suno error message, a defensive bail-out in the poller after 5 consecutive data:null responses, and a refusal to start the loop on empty/"undefined"/"null" task IDs. Silent failures hide in the gap between "the API returned 200" and "the API actually did the thing."

The 30-second mobile splash screen. Our app-init flow await-ed Firebase Auth and a Promise.all of three Firestore reads before dismissing the splash. On mobile, every one of those calls waits on the App Check ReCaptcha V3 token, which iOS Safari ITP and some carrier networks make slow or impossible. Cumulative result: 30–45 second blank-screen splash. Fix: dismiss the splash in ~150ms regardless of network, fire auth/settings/stats/ticker in background, let React state populate naturally as each call returns. Mobile time-to-interactive went from 30–45s to under 1s. Sequential awaits look innocent and cost you everything.

The donations collection name mismatch. The Firestore security rule permitted /donations/{docId}, but the client code targeted /donors/{docId}. Every admin write hit default-deny and returned a permission-denied error. The Admin SDK on the server bypasses rules, so all server-side writes worked fine — only the admin UI silently failed, for months. Default-deny is correct, but its error mode is invisible to the developer who wrote the rule.

Accomplishments we're proud of

  • The Claim + @id + citation + disambiguatingDescription schema pattern. A recent independent GEO audit called it "model-worthy" — most news sites stop at NewsArticle; nesting individually-anchored, individually-cited Claim items per perspective gives AI extractors a clean way to quote a single regional view and resolve its sources. We scored 99/100 on schema.
  • A fully public, no-auth API (/api/topics/recent) with OpenAPI 3.0.3, ai-plugin.json, and a 42K-word llms-full.txt. The platform is consumable by any AI agent the moment it exists.
  • IndexNow integration that pings Bing/Copilot/ChatGPT-via-Bing on every publish — new topics appear in those search surfaces within minutes.
  • Hein-built end-to-end. Solo founder, Amsterdam. No funding, no team. Every line of code, every prompt, every design decision.

What we learned

  • Google Search grounding is the unlock. Multi-perspective AI without grounding is just stylistic ventriloquism. With grounding, each perspective resolves to verifiable journalism.
  • Transparency about AI is a feature, not a confession. Our /methodology, /editorial-standards, and /corrections pages explicitly name Gemini 3 Flash, document the pipeline, and admit limits. The audit's highest-scoring dimension (Trustworthiness 82/100) was a direct result.
  • GEO is its own discipline. Optimizing for AI citation requires different work than ranking on Google search — llms.txt, schema density, <cite> tags, server-rendered crawler snapshots, AI-bot allowlisting. We had to build the playbook as we went.
  • Default to side-by-side, not consensus. The temptation when designing multi-perspective AI is to "average" toward neutrality. The harder, more useful design is to preserve regional framings — including ones we disagree with — and let the reader do the comparison.

What's next

Wikipedia entity stub, founder Wikidata Q-ID linked via founder from Q139814931, YouTube channel auto-publishing the daily 20-second topic videos, and resolving the ~90 Vertex AI grounding redirect URLs per topic page back to canonical publisher URLs. On the product side: per-perspective question-form H2s ("What does the U.S. say about [topic]?") to feed Google AI Overviews directly, plus expanding the roster from 13 default-enabled to the full ~20 configured perspectives, reader-selectable.

The moonshot stays the same: every news story, every regional lens, every day — so anyone, anywhere, can read what the other side is actually saying.

Built With

  • express-on-firebase-app-hosting-(cloud-run-under-the-hood)
  • firebase
  • firestore
  • firestore-for-persistence
  • react
  • typescript
  • vite
Share this project:

Updates