The day's news on a slowly rotating 3D Earth. The globe tours the world, pausing over each newsworthy place to reveal the story in a sci-fi popup — then moves on.
EarthViewNews is a personal, localhost web app that turns the day's headlines into an ambient, sci-fi "situation room". A blue-marble Earth rotates calmly in space; as real stories are ingested, the globe flies to each story's location, a red pin drops, and a terminal-style panel scans open with the headline, a short summary, and the source. It's news as a slow, cinematic tour rather than an endless feed.
There's also a zoomed Countries view for touring a single country's stories.
- 🌍 Rotating 3D globe built with
globe.gl/three.js, blue-marble texture and starfield. - 📰 Automatic news tour — the camera travels to each story, reveals it for a reading window, then moves on.
- 🛰️ Sci-fi HUD — live ticker, queue sidebar, per-city clock and weather.
- 🗺️ Global & Countries scopes — watch the whole world or focus on one country.
- 🤖 Two ingest paths — a paid DeepSeek pipeline, or a free Claude-assisted path that costs nothing.
- 💤 Graceful cold start — with no stories (or no API key) the Earth just rotates calmly.
RSS feeds ──▶ Defuddle (clean) ──▶ DeepSeek / Claude ──▶ SQLite ──▶ API ──▶ Globe
extract article text geocode + summarize stories /api tour
The backend polls RSS feeds, cleans each article, asks a model to geocode and
summarize it, and stores confident results as Stories in SQLite. The frontend
fetches the current scope's stories and drives the Tour (travel → reveal →
next). See docs/PRD.md for the full product spec,
CONTEXT.md for the domain glossary, and docs/adr/
for architecture decisions.
- Node.js 20+ and npm
- Optionally, a DeepSeek API key (for the automatic paid ingest). The app runs fine without one — see below.
git clone https://github.com/phuocphn/EarthViewNews.git
cd EarthViewNews
npm install
cp .env.example .env # then add your DEEPSEEK_API_KEY (optional)
npm run devnpm run dev starts everything with one command: the backend (API + 5-minute
ingest cron) and the Vite frontend (which proxies /api to the backend). Open
the printed URL and you'll see the rotating Earth; once ingest has fetched some
news, the globe starts touring real stories. With an empty store it simply
rotates calmly.
Without a DEEPSEEK_API_KEY the app still runs — the globe rotates and the API
serves whatever is already stored — but no new stories are ingested.
The only paid call in the pipeline is the geocoding/summarization step. You can do that step in a Claude Code session instead, so ingesting news costs nothing. The normal DeepSeek pipeline is untouched — this is an extra, on-demand path.
To use it, blank DEEPSEEK_API_KEY in .env (or stop npm run dev) so the
5-minute paid cron doesn't run, then in a Claude Code session:
# 1. Fetch + clean all new Articles -> scripts/tmp/articles.json
npx tsx scripts/extract.ts# 2. Ask Claude to read scripts/tmp/articles.json and, for each Article,
# write scripts/tmp/analyses.json with one entry:
# { "i": <index>, "city", "country", "lat", "lng", "confidence", "summary" }
# Rules: keep confidence >= 0.5; summary 40-60 words in the Article's language.
# 3. Save the confident Stories, mark every URL seen, prune the 24h window
npx tsx scripts/insert.tsStep 2 is where Claude replaces DeepSeek — it reads the cleaned Articles and
returns the same {location, confidence, summary} shape the API expects. Per-run
files under scripts/tmp/ are git-ignored.
| Command | What it does |
|---|---|
npm run dev |
Backend (API + ingest) + frontend together |
npm run build |
Typecheck and build the frontend |
npm start |
Run the backend, serving the built frontend (production) |
npm run ingest |
Run one ingest pass manually |
npm run typecheck |
tsc --noEmit |
npm test |
Run unit tests (Vitest) |
Copy .env.example to .env (git-ignored). DEEPSEEK_API_KEY powers the
geocoding/summarization. Optional:
| Variable | Default | Purpose |
|---|---|---|
DEEPSEEK_API_KEY |
— | Geocoding/summarization (leave blank for the free path) |
EVN_PORT |
8787 |
API port |
EVN_TOUR_CAP |
30 |
Stories per tour loop |
EVN_DB |
— | SQLite path |
client/— the React +globe.glfrontendserver/— the backend: RSS → Defuddle → DeepSeek → SQLite ingest, plus the APIscripts/— the free, Claude-assisted ingest path (extract.ts+insert.ts)docs/— PRD, ADRs, and design notes
Contributions are welcome! Please read CONTRIBUTING.md for
the development workflow and coding guidelines, and open an issue for anything
larger than a quick fix. Bug reports and feature requests have
issue templates ready to go.
Released under the MIT License.