Back to Selected Work

    Personal Finance Tracker

    No backend, no account, and no fetch() call to anywhere but Hugging Face — once, on request.

    ReactTypeScriptIndexedDBViteView live

    What it does

    Import a statement, get it categorized, keep it local

    Transactions come in by manual entry or by importing a bank statement — CSV, Excel, OFX, or PDF, parsed off the main thread in a Web Worker. Every transaction runs through a categorization pipeline and lands in IndexedDB. There's no server: the whole app is a static bundle, works offline as an installable PWA, and — verified directly in the source, not just claimed in the README — there is not one fetch or axios call anywhere in the codebase.

    Architecture

    Rules do the work; a local model only covers the gaps

    Import

    CSV / XLS / OFX / PDF parsed in a Web Worker. Repeat imports from a known bank auto-map columns from a remembered profile.

    Normalize

    Strip bank noise, detect the payment channel (UPI/NEFT/IMPS/ATM/etc.) from the description text.

    Rule engine

    User rules first (always win), then ~98 precompiled system rules by priority tier — structural overrides, income signals, keyword categories. Compiled once, not per row.

    only if still uncategorized or low-confidence

    Hugging Face CDN · third-party

    Opt-in only. The one real network call in the app — downloads the 22MB MiniLM model once, then it's cached by the browser for every session after.

    On-device embedding model

    A real MiniLM sentence-transformer (transformers.js, ONNX/WASM) runs in a Web Worker, compares the description against 10 category prototypes by cosine similarity.

    IndexedDB

    Versioned schema (v1 → v5), a dedicated store per data type. The AI suggestion is surfaced, never auto-saved — the user confirms it.

    Worth being precise about

    What 'on-device AI' actually means here

    The rule engine handles the overwhelming majority of rows with zero ML involved — it's the default and only thing that runs unless the user opts into the AI feature. What's genuinely there: a real 22MB sentence-embedding model from Hugging Face, cached after the first download, matching uncategorized transactions against ten category prototypes by cosine similarity — not a fine-tuned classifier or a hosted LLM call. Confirmed transactions are remembered too, so recurring merchants stop needing the model at all.

    "Anomaly detection" and forecasting elsewhere in the app are plain statistics — a Z-score against a rolling baseline, linear extrapolation — not AI, despite living under the same menu.