A free, browser-only statistics tool for the whole analysis loop — from raw data to a publication-quality figure.
StatLens runs a real Python scientific stack — NumPy, SciPy, statsmodels, pingouin, lifelines — inside your browser via Pyodide and WebAssembly. ~45 statistical tests, eight typed data table types, and 12 chart types with automatic significance brackets.
Nothing to install. No server. Your workbooks are saved to your own Google Drive.
Statistical correctness is the top priority; UX polish is second. 44 analyses are pinned to golden reference values from SciPy, statsmodels, pingouin, and R — and re-verified in CI on every push.
Statistics software tends to be expensive, desktop-bound, or both. StatLens does the everyday work — put data in a typed table, run the right test, get a clean graph with significance brackets — in a browser tab, for free.
An in-app About page carries the same guide, kept verified against the engine.
The table type you pick determines which analyses are offered.
| Table | Use when | Example | Tests | Post hoc |
|---|---|---|---|---|
| Column | Comparing groups on one measurement | Control / Drug A / Drug B tumour volumes | One-sample t, Unpaired t (Student’s), Welch’s t, Paired t, Mann-Whitney, Wilcoxon, Sign test, Kolmogorov-Smirnov, One-way ANOVA, Welch’s ANOVA, Brown-Forsythe, Repeated-Measures ANOVA, Kruskal-Wallis, Friedman | Tukey, Dunnett (vs control), Dunnett’s T3, Games-Howell, Dunn, Bonferroni, Šídák, Holm-Šídák |
| XY | One variable measured against another | Dose vs response; time vs signal | Pearson, Spearman, Simple linear regression, Nonlinear curve fitting (exponential, Michaelis-Menten, 4PL, Gaussian, polynomial, Boltzmann), Deming regression, Simple logistic regression, Area under curve, LOWESS, Spline, Smooth, Integrate, Differentiate | — |
| Grouped | Two factors at once | Drug × Dose on blood pressure | Two-way ANOVA (Type III), Repeated-Measures two-way, Mixed-effects, ART ANOVA (non-parametric) | Tukey on marginal means |
| Contingency | Counts in categories | Exposed / Unexposed × Case / Control | Chi-square (± Yates), Fisher’s exact, McNemar’s, Odds and risk ratios, Diagnostic (sensitivity / specificity) | — |
| Survival | Time until an event | Days to relapse by treatment arm | Kaplan-Meier, Log-rank (Mantel-Cox), Gehan-Breslow-Wilcoxon, Hazard ratios, Cox regression | Pairwise log-rank (Holm) |
| Parts of Whole | One whole split into parts | Cell counts per phenotype | Fraction of total, Chi-square goodness of fit, Binomial test | — |
| Multiple Variables | Many variables per subject | Age, BMI, dose, response per patient | Correlation matrix, Multiple linear regression, Multiple logistic regression, Poisson regression, PCA, Three-way ANOVA | — |
| Nested | Subsamples within groups | 3 wells per animal, 4 animals per group | Nested t-test / nested one-way ANOVA | Tukey |
Analyses report effect sizes (Cohen’s d, eta², mean differences), confidence intervals, and degrees of freedom, alongside a Markdown results report.
Bar + error bars · Box & whisker · Violin · Raincloud · Scatter · Strip · Jitter · Beeswarm · Horizontal box · Range / dumbbell · CI forest · Kaplan-Meier step
All support significance brackets and asterisks, computed and laid out automatically. Error bars (SD / SEM / CI) and regression overlays with confidence bands. Light/dark theme, 15 bundled font families.
Export: 600-DPI PNG with correct DPI metadata and embedded fonts, or SVG.
StatLens is a 100% client-side single-page app. Nothing runs on a server.
┌──────────────────────── Browser tab ─────────────────────────┐
│ │
│ React 19 + TypeScript (Vite 8) │
│ ├── ag-grid ............ spreadsheet data tables │
│ ├── visx / d3 .......... chart rendering (SVG) │
│ └── Radix + Tailwind ... UI (shadcn/ui) │
│ │ │
│ │ postMessage │
│ ▼ │
│ Web Worker ──► Pyodide 0.26.1 (WebAssembly) │
│ ├── numpy, pandas, scipy │
│ ├── statsmodels, scikit-learn │
│ └── pingouin, scikit-posthocs, lifelines │
│ └── analysis_engine.py (3.1k lines) │
│ │
└──────────────┬───────────────────────────────────────────────┘
│ HTTPS
▼
Google Drive (drive.file scope) ── your workbooks, your account
The parts that matter:
src/stats/analysis_engine.py (~3,100 lines) is the single source of statistical truth. It executes under Pyodide inside a Web Worker, so heavy analyses never block the UI.Loading engine… → Engine ready. After that, the Service Worker caches everything locally, meaning the app loads instantly on subsequent visits (even offline) and updates silently in the background (StaleWhileRevalidate).SharedArrayBuffer, so no COOP/COEP headers are required and the whole app can be served as plain static files.*.statlens, saved into a StatLens folder in your Drive.| Layer | Choice |
|---|---|
| Build | Vite 8 (Rolldown), TypeScript 6 |
| UI | React 19, Tailwind CSS 3.4, Radix UI / shadcn/ui, lucide-react |
| Grid | ag-grid-community 35 |
| Charts | visx 4, d3-scale / d3-shape / d3-array |
| Routing | react-router-dom 7 |
| Validation | zod 4 |
| Stats | Pyodide 0.26.1 → NumPy, pandas, SciPy, statsmodels, scikit-learn, pingouin, scikit-posthocs, lifelines |
| Auth | Google Identity Services |
| Lint | oxlint |
| Tests | vitest + a Python-side CI matrix |
The project’s central rule, from Agents.md:
Statistical correctness is non-negotiable. Every statistic/test has a unit test against known reference values (SciPy/statsmodels/pingouin/lifelines docs, R, or published worked examples) with sensible tolerance. Never change an algorithm without updating tests + comments.
What that looks like in practice:
golden_values.json — 44 analyses pinned to reference outputsreference_R/ — the R scripts that generated those expected valuesrun_audit.test.ts — boots Pyodide and runs the real engine against the golden values, not a mockcharts/__tests__/geometry.test.ts — chart geometry mathsdocs/AUDIT_MATRIX.md — per-analysis status: does it run, is it golden-tested, does it go through the engine path.github/workflows/stats-tests.yml runs on every push and PR, against a Python stack pinned to match what the Pyodide worker installs — so CI and the browser can’t silently diverge:
tsc passAn analysis without a reference test is considered unfinished.
StatLens, as *.statlens JSON files.drive.file scope, which grants access only to files the app itself creates. It cannot read the rest of your Drive.localStorage for the session and revoked on sign-out.StatLens/
├── apps/web/src/
│ ├── App.tsx # routes, header, engine bootstrap
│ ├── pages/AboutPage.tsx # in-app about + usage guide
│ ├── stats/
│ │ ├── analysis_engine.py # ★ all statistics live here (~3.1k lines)
│ │ ├── pyodide.worker2.ts # Web Worker: boots Pyodide, runs the engine
│ │ ├── engine.ts # main-thread client for the worker
│ │ └── __tests__/ # golden-value oracle tests
│ ├── charts/
│ │ ├── GraphEngine.tsx # chart dispatch
│ │ ├── {Column,XY,Survival}Charts.tsx
│ │ ├── SignificanceLayer.tsx # significance bracket layout
│ │ └── geometry/ # pure, testable chart maths
│ ├── components/
│ │ ├── dashboard/ # workbook list
│ │ ├── workspace/ # grid, analyse panel, results, graph settings
│ │ └── ui/ # shadcn/ui primitives
│ ├── data/ # auth, Drive API, sheet templates
│ ├── lib/exportGraph.ts # PNG / SVG export
│ └── types/workbook.ts # zod schemas for the workbook format
├── apps/web/scratch/ # Python contract + verification scripts (run by CI)
├── docs/
│ ├── PRD.md # product requirements
│ ├── AUDIT_MATRIX.md # every analysis + its runtime/golden status
│ └── *.md # statistical references (post hoc, curve fitting…)
├── tests/ # Python-side oracle tests
└── Agents.md # project charter & engineering rules
Built on the shoulders of Pyodide, SciPy, statsmodels, pingouin, scikit-posthocs, lifelines, visx, and AG Grid.
Built with Claude and Antigravity.
MIT — free to use, modify, and distribute; just keep the copyright notice.