MSN-002 · 2025 · chrome, gemini-nano, on-device-ml
Chrome extension using the Prompt API and Gemini Nano for on-device text classification. Runs entirely in the browser with no server-side inference.
Smart ScreenTime is a Manifest V3 Chrome extension that uses the browser's built-in Gemini Nano model to classify web content into one of twelve categories, then enforces a per-category policy based on a user-defined focus schedule. No text leaves the device at any point in the loop.
The standard approach — extracting page content and sending it to a remote inference endpoint — creates a persistent record of browsing context on a third-party server. Chrome ships with Gemini Nano already installed and exposes it through the experimental Prompt API. Using that instead costs nothing in privacy and adds no latency from a network round-trip. The trade-off is that the model is smaller, so I built a heuristic fallback for cases where the Prompt API is unavailable or returns a confidence score below the configured threshold.
The content script runs at `document_idle` with a 500ms debounce to handle SPA navigation. It extracts a cleaned text snippet (capped at ~1,200 characters) from semantic content elements — ``, and signals like `
The service worker resolves each request through a five-tier precedence chain before ever reaching the model:
1. Temporary allows — 15-minute user overrides stored with an expiry timestamp 2. Allowlist — permanent hostname exceptions 3. Blocklist — permanent blocks 4. Cache — TTL varies by category: 2 hours for social feeds, 48 hours for video and gaming, 7 days for docs and reference content. Cache keys encode `hostname:pathBucket:snippetSignature:policyContext` to handle pages that change content without changing URL. 5. Prompt API — Gemini Nano, `temperature: 0.1`, `topK: 1` for deterministic output
The Prompt API call never leaves Chrome. Gemini Nano is already installed on the device; the extension just sends a structured prompt and parses the JSON response. The model returns `{ category, confidence, reasons }`.
Classification alone doesn't decide anything — the result passes through a policy matrix that maps each of the twelve categories to `allow` or `block` depending on whether the current time is inside the user's focus window. In focus mode, categories like `social/feeds`, `entertainment`, and `gaming` block; `educational/lecture`, `work/productivity`, and `news` allow. In leisure mode, everything allows except the blocklist.
If the model returns a confidence score below the configured threshold (default 0.75), the extension falls back conservatively: block during focus, allow during leisure.
When the Prompt API is unavailable — during model download or in environments where it hasn't been enabled — the service worker uses a keyword and domain pattern matcher. Social sites score 0.85 confidence on a domain match alone; a site-plus-keyword combination lands at 0.75–0.80; no match returns 0.30 with the `unclear` category, which the policy matrix treats as block during focus.
When a page is blocked, the content script injects a modal over the page using a stylesheet loaded via `web_accessible_resources`. The overlay shows the category name, confidence percentage, and the model's reasoning string. Three actions are available: allow the site for 15 minutes, add the hostname permanently to the allowlist, or dismiss. The modal implements a focus trap with Tab/Shift+Tab cycling, keyboard shortcuts (Esc, Enter, A), and ARIA roles for accessibility. Thumbs-up/thumbs-down feedback buttons store signals for future personalization, though active feedback-driven re-ranking isn't wired up yet.
The Chrome Prompt API (`globalThis.ai.languageModel`) is behind an origin trial flag. The extension requires Chrome 127+ with the flag enabled, or enrollment in the origin trial.
- Chrome Prompt API (Gemini Nano) — on-device LLM inference - Chrome Extensions Manifest V3 — service worker, content scripts - `chrome.storage.local` — config, decision cache, feedback log - Heuristic classifier — keyword + domain pattern matching fallback - No backend, no external dependencies