A Chrome extension for people who want to own their taste across films, shows, and books — not just consume it.
- The Philosophy
- What Makes Subsume Unique
- Three Acts of Interaction
- Feature Overview
- Architecture
- Security
- Setup & Local Run
- Chrome Web Store
- Attribution
- License
Modern content discovery is built to maximize aggregate runtime, not authentic taste. Recommendations get flattened into demographic clusters — "people who watched X also watched Y" — and the individual eye that noticed the lighting, the cut, the performance, or the sentence that stopped the room, gets lost.
Subsume is not a tracker. It's not a notes app. It's a private sanctuary for films, shows, and books — built to feel like stepping into a theatre lobby or a quiet reading room: anticipation, immersion, reflection, memory.
The goal isn't feature usage or data entry. It's quality of reflection.
If you leave a film or a book with a deeper emotional connection to it than when you walked in, Subsume did its job.
-
Auteur-First Discovery — Traditional apps sort by genre (Drama, Action). Subsume treats filmmakers and key crew — directors, cinematographers, writers, actors — as the primary artistic unit. Track Roger Deakins' lighting arc or Charlie Kaufman's thematic throughline, with your own scores layered across their full body of work.
-
Museum Catalogue Plaques — Discovery happens everywhere you already browse. Subsume scans posters and book covers on any webpage and injects a quiet, typographically refined rating badge (
★ 8.4 │ Reflect) with a spring-expansion hover reveal. Every injection lives in its own isolated Shadow DOM container, so it never touches or breaks host-site CSS. -
Poetic Capture Canvas — Adding something to your sanctuary starts with one question: "What stayed with you?" Only after you write does the interface reveal intent buckets and a rating scale. Your emotional recall is the primary artifact here — not the IMDb score.
-
Hardcover Library Archive — Your library isn't a grid of rows. It's an editorial archive grouped by living intent — Keep This Memory, Revisit This Month, Wishlist — with your own
emotionalRecallsurfaced as an italicised excerpt on each spine. -
Auteur Screenplay Dock — A floating reflection notepad, isolated the same way as the plaques, that stays with you on any page for recording narrative parallels and screenplay ideas in the moment they occur to you.
-
Zero-IPC Watchlist Acceleration — Your watchlist lives as a synchronous, in-memory O(1) Set inside the content script, so hover cards and badge states resolve instantly — no IPC round-trip, no IndexedDB read on every hover.
-
Two-Stage Contextual AI Prompting — A 6.5 on IMDb might be a 10 for you. Subsume builds a taste profile from your highest-rated qualitative notes, queries an LLM of your choice (OpenAI, Anthropic, Gemini) for matches, and optionally makes a second pass to cluster results by the reference seeds you loved most.
-
Decoupled, SOLID-First Architecture — Background logic is split into single-responsibility domain handler maps. AI providers sit behind a polymorphic adapter interface. Every boundary is typed and defensive — built the way I'd want to hand this off to another engineer, not just the way that gets a demo working.
Subsume scans any webpage for posters, titles, and books (including ISBNs) and injects a museum catalogue plaque: title, rating, and a Reflect trigger. Hover any detected work for an instant card — no tab switching.
Reflect opens the Poetic Capture Canvas — full-screen, the poster art blurred softly behind a single centred prompt: "What stayed with you?" Only once you start writing does the canvas reveal intent selectors and a 1–10 scale. Emotion before metadata, always.
The Hardcover Library Archive organizes everything you've captured — each title a spine in an editorial collection, searchable and filterable by auteur-tagged crew, sortable by recency or resonance. Click a spine for the full DetailModal — your complete record of that encounter.
| Feature | What It Accomplishes | Technical Implementation |
|---|---|---|
| Museum Catalogue Plaques | Rating badges with hover expansion on any webpage poster or cover | Open Shadow DOM, MutationObserver, spring CSS transitions |
| Poetic Capture Canvas | Emotion-first capture with progressive disclosure | Preact, URL param routing (?act=capture), focus-pull blur keyframes |
| Hardcover Library Archive | Editorial archive grouped by sanctuaryIntent with emotionalRecall excerpts; All / Screen / Books filters |
Preact, useMemo intent filtering, medium filter, isMountedRef async safety |
| Book detection & catalogue | Notice books on any page; resolve works via Open Library (no key) with optional Google Books enrichment | Content-script stages (JSON-LD, ISBN, domain adapters), Open Library + Google Books background clients |
| Auteur Screenplay Dock | Floating reflection notepad on any page | Shadow DOM, toggle collapse/expand, destroy() lifecycle |
| Chronological Filmography Tracking | Follow directors, DPs, actors, writers across their full body of work | TMDb Person API, IndexedDB people store |
| Cross-Site Hover Cards | Instant synopsis and status on film, show, or book titles | Isolated DOM injection, debounced pointer controllers, O(1) cache |
| Contextual LLM Recommendations | AI discovery from your actual taste profile and notes | Two-stage prompting pipeline, OpenAI / Anthropic / Gemini adapters |
| Weekly Automated Digests | Curated new release picks across streaming subscriptions | Chrome background alarms, dynamic rule/AI hybrid curation |
| Google Drive Sync | Full library backup and restore via Google Drive | OAuth 2.0, multipart Drive API upload/download |
Design authority: UI implementation follows
CINEMATIC_JOURNAL_DESIGN_SPEC.mdandsrc/shared/tokens.css. Those sources supersedebrand.mdon conflicts (typography, tokens, motion).
┌────────────────────────────────────────────────────────┐
│ Any Website (Content Layer) │
│ MutationObserver → Image Scanner & Title Matcher │
│ Shadow DOM: Museum Plaques · Hover Cards · Dock │
└──────────────────────────┬─────────────────────────────┘
│ O(1) Sync Cache / IPC Message
┌──────────────────────────▼─────────────────────────────┐
│ Background Service Worker │
│ Domain Handler Maps (Library · LLM · Prefs · Sync) │
│ Polymorphic AI Adapters (OpenAI · Anthropic · Gemini) │
└──────────────────────────┬─────────────────────────────┘
│ Atomic Transactions
┌──────────────────────────▼─────────────────────────────┐
│ Persistent Storage (IndexedDB) │
│ media · library · people · tmdbCache · alerts │
│ v2 schema: sanctuaryIntent · emotionalRecall │
│ scriptParallels · originalScreenplaySparks │
└────────────────────────────────────────────────────────┘
- Single Responsibility (SRP): Domain handler registries (
libraryHandlers,llmHandlers,syncHandlers,settingsHandlers) replace monolithic switch statements. Backgroundindex.tsstays at 41 lines by design. - Open/Closed (OCP): AI providers implement a common adapter interface — adding Ollama or DeepSeek requires zero changes to the core pipeline.
- Strict TypeScript: No
anytypes in production code. Allunknowninputs are narrowed at boundaries. - Memory Safety: Every Shadow DOM manager (
MuseumPlaqueManager,AuteurScreenplayDock) implements an explicitdestroy()lifecycle. Every async hook uses anisMountedRefcancellation flag. - Structured Telemetry: All diagnostic messaging routes through a typed
loggerutility. - Test Coverage: ~222+ unit tests (Vitest). Run
npm testfor the live count.
Subsume is a client-side Chrome extension. There is no backend proxy — API keys and library data stay on your machine. Understand these tradeoffs before enabling LLM features or distributing a fork.
| Topic | Behavior |
|---|---|
| Where keys live | TMDb, OMDb, optional Google Books, and LLM API keys are stored in IndexedDB (subsume-db) as part of UserPreferences. They are not encrypted at rest. |
| Who is responsible | You. Keys never leave your browser except when the extension calls the providers you configure. Treat your machine and Chrome profile as trusted. |
| Content-script exposure | API keys are never sent to content scripts. GET_CONTENT_PREFS returns feature toggles only (see buildContentPrefs()). |
| Export/backup | Library export excludes API keys. Only media and library records are included. |
LLM calls (OpenAI, Anthropic, Gemini) are made directly from the background service worker using your API keys. Prompts are built from your local library — not from arbitrary page DOM — but short note excerpts (≤100 chars) may be included in taste-profile prompts.
Anthropic browser header: When using Anthropic, the extension sends anthropic-dangerous-direct-browser-access: true, which Anthropic requires for API requests originating from a browser context (MV3 service worker). Without it, direct client calls fail. The tradeoff: your API key is used client-side and is visible to anyone with access to the extension bundle or your browser profile.
Recommendations:
- Use personal API keys for solo or portfolio use.
- Do not ship a public Chrome Web Store build that embeds shared or enterprise keys.
- For team or enterprise distribution, route LLM calls through a server-side proxy that holds keys and enforces rate limits — do not rely on client-side key storage.
Drive backup uses a Web application OAuth client (implicit grant via chrome.identity.launchWebAuthFlow). The client ID lives in src/shared/googleDriveOAuth.ts — not in manifest.json. Production OAuth is registered for the fixed extension ID pinned by the manifest key (ehbkfdgpbemaimepgeeflenhbbpgokoj). Forks need their own Google Cloud OAuth client and redirect URI. See Setup → Google Drive OAuth and docs/GOOGLE_DRIVE_SETUP.md.
Subsume injects on all http:// and https:// pages so discovery works on any site you browse. That broad surface is deliberate, but hostile pages must not be able to exfiltrate your full library or exhaust API quotas.
| Mitigation | What it does |
|---|---|
| Message allowlist | Content scripts may only call a small set of background messages (GET_CONTENT_PREFS, RESOLVE_POSTER, CHECK_LIBRARY_STATUS, etc.). GET_LIBRARY, SET_USER_NOTES, and settings/key messages are extension-UI only. |
| Scoped library checks | Hover cards call CHECK_LIBRARY_STATUS per mediaId when a card opens — never the full watchlist. |
| Per-origin poster budget | Max 10 RESOLVE_POSTER calls per origin per content-script session; additional resolves are skipped and logged. |
| Poster query clamping | Ancestor-text poster queries are capped at 60 characters and require ≥2 words before hitting TMDb. |
| API key stripping | GET_CONTENT_PREFS returns feature toggles only — no TMDb/OMDb/LLM keys in the content layer. |
| Page reflection storage | The Auteur Reflection Dock saves notes in chrome.storage.local keyed by hostname + pathname, not via fake mediaId library writes. |
| Sync listener hardening | LIBRARY_UPDATED handlers verify sender.id === chrome.runtime.id before mutating in-memory caches. |
| Lifecycle teardown | HoverCardManager, MuseumPlaqueManager, and AuteurScreenplayDock expose destroy(); the content entry point tears down on pagehide. |
You can blacklist domains in Settings → Disabled Domains to disable all content-script features on specific sites.
- Node.js 18+ & npm
- Google Chrome or Brave Browser (v120+)
- TMDb API Key (Free at themoviedb.org/settings/api) — required for film/TV catalogue features
- Open Library — books work out of the box with no key
- OMDb API Key (Optional) (Free at omdbapi.com/apikey.aspx)
- Google Books API Key (Optional) — cover and description enrichment
- LLM API Key (Optional) (OpenAI, Anthropic, or Google Gemini)
git clone https://github.com/harshabala/Subsume.git
cd Subsume
npm install
npm run buildProduction builds: Drive sync is preconfigured for the stable extension ID from the manifest
key. Load unpacked from a cleandist/build of this repo and Connect Google Drive should work for the registered OAuth client.
OAuth is not configured via a YOUR_CLIENT_ID_HERE placeholder in manifest.json. The Web client ID and scopes live in:
src/shared/googleDriveOAuth.ts—GOOGLE_CLIENT_ID, scopes, registered redirect URIdocs/GOOGLE_DRIVE_SETUP.md— Google Cloud console steps and troubleshooting
Forks: create your own Web application OAuth client in Google Cloud Console, enable the Drive API and the scopes listed in the setup doc, and set the authorized redirect URI to https://<your-extension-id>.chromiumapp.org/ (from chrome.identity.getRedirectURL() / Settings → Data). Update GOOGLE_CLIENT_ID (and the registered URI constant) for your fork. Do not reuse the upstream production client for a public fork with a different extension ID.
- Navigate to
chrome://extensions(orbrave://extensions). - Enable Developer mode (toggle, top right).
- Click Load unpacked.
- Select the
dist/folder inside your project directory. - Click the Subsume icon → Settings → paste your TMDb API key.
A short GIF or still of the Poetic Capture Canvas or a museum plaque hover reveal helps readers orient in seconds (see the placeholder at the top of this README). For Chrome Web Store listing shots, use the sizes and suggested frames in store/screenshots/README.md and place finals under store/screenshots/ (promotional tiles under store/assets/). Screenshots must be captured by a human from a running extension until automated generation exists.
Subsume is prepared for Chrome Web Store packaging and review. Status and materials:
| Item | Location / command |
|---|---|
| Package upload zip | npm run package — builds dist/, then zips package contents to subsume.zip (source maps excluded; see scripts/package-extension.mjs) |
| Privacy policy | docs/PRIVACY.md — host this URL (or a rendered copy) for the CWS privacy field |
| Listing / review notes | store/ — MANIFEST_NOTES.md (permission rationale), assets/ (promo tile sizes), screenshots/ (required screenshot frames) |
| Stable extension ID | Manifest key pins ID ehbkfdgpbemaimepgeeflenhbbpgokoj for OAuth redirect URIs |
Before a store submission: run npm run ci (or npm test + npm run build), capture screenshots per store/screenshots/README.md, and confirm Drive OAuth against the fixed ID.
Issues and PRs are welcome. Please open an issue before submitting larger changes so we can align on approach first. See CONTRIBUTING.md for setup, pre-ship checks, and process. This project follows a short CODE_OF_CONDUCT.md.
Subsume stands on the shoulders of incredible open-source tools and open data providers:
- TMDb (The Movie Database): For comprehensive cinematic metadata, cast/crew hierarchies, and high-resolution poster imagery. (Notice: This product uses the TMDb API but is not endorsed or certified by TMDb.)
- Open Library: For book works, editions, search, and covers (default book catalogue; no user key required). (Subsume is not affiliated with the Internet Archive or Open Library.)
- Google Books (optional): For cover and description enrichment when the user supplies an API key.
- OMDb API: For supplementary IMDb score aggregation.
- Preact & Vite: For ultra-fast, lightweight UI rendering and bundle optimisation.
- idb: For robust Promise-based IndexedDB transaction wrapping.
- Lucide Icons: For crisp, modern UI iconography.
- Newsreader & Outfit (Google Fonts): For the editorial typographic identity of the sanctuary.
- Vitest: For the unit test suite (~222+ tests; run
npm testfor the current count).
MIT — see LICENSE for the full text.
Subsume was conceived, architected, and directed by Harsha Balakrishnan.
I made this because I wanted it to exist. Not for a market, not for a metric — for me, and as my thanks to cinema and the page for keeping the park alive inside of me.
Every architectural pattern, SOLID abstraction, defensive boundary, and Shadow DOM interaction was directed, reviewed, and refined by hand — AI-assisted development was the hands on the keyboard, not the eye behind the decisions.
- GitHub: @harshabala
- Email: harsha16balakrishnan@proton.me
Taste is an active pursuit — on the screen and on the page. Own it.