A visual field guide to how people actually talk about meditation.
MindSpace OS turns 2,899 posts and comments from meditation on Reddit (Jan 2024 – Jun 2025) into a navigable field guide — emotional maps, sentiment weather, theme networks, and quarterly shifts. Built to see what a practice community sounds like when nobody's selling anything.
Live site: https://mindspaceos.com (Cloudflare Pages preview at mindspace-os.pages.dev as fallback).
The project ships as two sibling surfaces from one repo:
- Editorial site (
site/) — Astro static build, the public reading experience. Hosted on Cloudflare Pages. All four interactive chart pages are baked into self-contained static HTML at build time, so the editorial site has zero runtime dependency on Streamlit (no cold-start, no hibernation, ~1.5s first load). - Streamlit app (
Homepage.py+pages/) — the source of truth for chart logic (Plotly figures, custom JS visualizations, data wiring). The staticsite/public/charts/*.htmlembeds are regenerated from this code byscripts/build_chart_figures.py. The Streamlit app at mindspaceos.streamlit.app still works for direct-link visitors and as a development surface, but the editorial site no longer iframes it.
Four interactive pages, accessible from the sidebar:
| Page | What it shows |
|---|---|
| Emotion Pulse | UMAP map where posts cluster by emotional vocabulary (via GoEmotions). Frustration, awe, curiosity each occupy their own region. |
| Community Dynamics | Sankey diagram — poster emotional archetype flowing into commenter emotional archetype. |
| Community Weather Report | 18 months of sentiment trends across topics, rendered as weather metaphors (sunny days, storms). |
| Inner Life Currents | Temporal network view — how theme connections shift quarter by quarter. |
Community Dynamics and Community Weather Report together show who meets whom and how the mood shifts over time — the two "Community" pages. Inner Life Currents complements them with a temporal network of theme co-occurrences.
The dominant emotional register on r/meditation isn't peace. It's struggle tightly coupled with curiosity. The community's shared vocabulary is closer to "trying again" than "finding bliss."
That's what a practice community actually sounds like.
- Astro 6 — static-site framework.
output: 'static', every page rendered to plain HTML at build time. Component model + content collections power the editorial pages;getStaticPathsgenerates the four/explore/*routes fromdata/canonical.json. - Tailwind CSS 4 (via
@tailwindcss/vite) — utility-first styling, theme tokens declared in@themeblocks rather than a separate config file. Time-of-day gradient theming + ripple-on-hover animations ported from the original meditation-circle design language. - @fontsource/* — self-hosted webfonts: Inter (UI + display), Source Serif 4 (editorial body + pull-quotes), JetBrains Mono (eyebrows + brand mark). Imported in the layout so they're inlined and don't pop in.
- @astrojs/sitemap — generates
sitemap-index.xml+ per-section sitemaps at build. - @astrojs/cloudflare — Cloudflare Pages adapter (declared in deps; ready to flip from
static→serverwhen dynamic OG cards land). - Plotly.js — loaded from CDN (
cdn.plot.ly/plotly-2.35.2.min.js) by the four static chart HTMLs. Cached across pages after first load. Powers Emotion Pulse (UMAP scatter + radar overlay), Community Dynamics (Sankey), and Inner Life Currents (force-directed temporal network). Community Weather Report uses pure CSS animations + a hand-rolled Cardinal-spline sparkline; no Plotly. - Cloudflare Pages — static hosting. Project
mindspace-os. Connected to GitHub for build-on-push.
- Bun +
bun:test— JS test runner for build-output assertions (route presence, OG meta, canonical-data sync, no-Streamlit-leak guard, static chart self-containment, no-inline-ARCHETYPE_COLORS-in-Python). 54 tests acrosssite/tests/. - Python 3.11 + pandas + numpy + pyarrow — drive
scripts/build_chart_figures.pywhich bakes the four static chart HTMLs (Plotly trace construction, hover-text formatting, weather-region positioning, all 6 quarters of temporal-network payloads inlined into one file). - Plotly (Python) — only used at build time, to construct trace JSON consumed by the static HTMLs. No Plotly Python in production.
- DuckDB + Parquet aggregates in
precomputed/— the data layer feeding the chart bake.
- Streamlit + Plotly —
Homepage.py+pages/*.pyare the canonical home for chart logic (figure construction, hover templates, custom JS visualizations). Hosted at mindspaceos.streamlit.app for direct-link visitors and as a development surface. The editorial site does not iframe Streamlit at runtime —scripts/build_chart_figures.pyre-implements the Plotly/HTML output and writes self-contained files intosite/public/charts/. When chart logic changes inpages/*.py, the build script needs the same change applied (it deliberately doesn'timportfrom the Streamlit modules to avoidst.set_page_configside effects).
- GoEmotions — Google Research's 27-emotion classifier; ran over the r/meditation corpus to produce per-post emotion scores.
- UMAP — dimensionality reduction on the GoEmotions embeddings → the
umap_x/umap_ycolumns Emotion Pulse plots. - Gaussian Mixture Model — clustered the UMAP embedding into the 5 emotional archetypes.
- TypeScript / Astro — the editorial site (
site/). - Python 3.11 — chart-bake script + Streamlit app + data pipeline.
- CSS / HTML — hand-tuned for the four chart HTMLs (CSS keyframe animations, flex layouts, custom Cardinal-spline sparkline).
git clone https://github.com/minyansh7/MindSpace-OS.git
cd MindSpace-OS
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
streamlit run Homepage.pyStreamlit will print the local URL and open it in your browser. Navigate between pages via the sidebar.
cd site
npm install
npm run dev # local dev server with HMR
npm run build # static build into site/dist/
npm run preview # serve the prod build locallyThe Astro build is what ships to Cloudflare Pages. The four static chart embeds it serves are baked by python3 scripts/build_chart_figures.py and live under site/public/charts/*.html — re-run that script whenever the chart palette, layout, or data changes.
A Dockerfile is included for containerized deploys of the Streamlit app. Note: the current CMD references app.py — update to Homepage.py before use (or rename the entry point). Streamlit Cloud runs Homepage.py directly and does not use the Dockerfile.
.
├── Homepage.py # Streamlit entry point
├── pages/ # Streamlit multipage auto-discovers these
│ ├── 0_Emotion_Pulse.py
│ ├── 1_Community_Dynamics.py # Poster → Commenter Sankey
│ ├── 2_Community_Weather_Report.py
│ └── 3_Inner_Life_Currents.py # temporal network
├── site/ # Astro editorial site (Cloudflare Pages)
│ ├── src/ # pages, components, layouts, lib
│ ├── public/ # static assets, OG cards, baked chart embeds
│ ├── tests/ # bun:test build + canonical-data assertions
│ └── astro.config.mjs
├── data/canonical.json # single source of truth — archetypes, post counts, page metadata, essays. Read by both Streamlit (Python) and Astro (TS).
├── scripts/
│ ├── _canonical.py # shared loader: ARCHETYPE_COLORS / TOPIC_MAPPING from canonical.json
│ ├── build_chart_figures.py # bakes site/public/charts/*.html embeds
│ └── build_precomputed.py # generates precomputed/ Parquet aggregates
├── precomputed/ # Parquet aggregates (topics, clusters, timeseries)
├── assets/ # Streamlit page icons, hero images
├── archive/ # historical page versions — not rendered
├── CLAUDE.md # design-intent notes (naming, color palette, typography, deploy)
├── docs/ # long-form writeups
└── requirements.txt
See CLAUDE.md for the editorial layer:
- Why each page is named what it is (and what it used to be called)
- The canonical cluster → color mapping (same seven themes across Inner Life Currents and Web)
- Typographic conventions (eyebrow labels, page H1s, hover text wrapping)
- Session-state plumbing across time-trend pages
The design pass that produced the current naming family and stripped decorative noise is documented in a long-form retrospective at docs/publish_draft.md.
- Source: r/meditation public posts & comments, Jan 2024 – Jun 2025
- Count: 2,899 posts and comments (canonical, post response-pattern filter). 2,977 pre-filter, documented in
data/canonical.jsonfor reproducibility. - Processing: emotion classification → UMAP clustering → theme grouping → temporal binning by quarter
- Storage: Parquet aggregates under
precomputed/for app runtime
Raw Reddit data is not redistributed. The precomputed/*.parquet files contain the processed analytical output used by the app.
- Built by Minyan Shi at MinyanLabs
- Emotion classification based on Google Research's GoEmotions taxonomy
- Data sourced from Reddit community posts
Source available for personal / educational exploration. Please reach out before commercial use or redistribution of the analytical outputs.
© 2026 MinyanLabs