Empty Words Unification Plan
The proposal in one line
Fold deep-truth-guides (prose home + gated library) and deep-truth-engine (induction experiences + reader) into a single site, owned by the React SPA, where the first encounter is the induction, the library is paywalled after three free writings, users hold a pin-and-username identity, and every reading session is tracked into an analytics surface built in the house monochrome terminal aesthetic.
The thesis Empty Words is establishing
Understanding nothing is everything. The machinery of nothing is the root of every other machinery on the site. A reader who has seen their own wanting, their own attention, their own fear, and their own dissolution has crossed the gap. The site exists to walk the reader to that crossing and then to keep the crossing visible so it compounds.
The site is therefore not a blog. It is a induction loop. The map does not work unless the reader has first felt the territory.
What exists today
deep-truth-guides (Jekyll, GitHub Pages)
- Static markdown. Parable home page. Access gate via SHA-256 hash in page source, unlock stored in localStorage.
- 21 guides in THE_MACHINERY_OF_*.md format with front matter, OG images, arc-next linking.
- Just shipped: PWA manifest, service worker, install button, update modal, mobile reading tuned.
- URL: ladiossato.github.io/deep-truth-guides/
deep-truth-engine (React 19 + Vite + Three.js, also GitHub Pages)
- SPA with phase state machine (gateway → select → experience → reading → stats).
- FocusGateway (white dot, configurable timer).
- 16 per-guide induction experiences already scaffolded (Desire, Mastery, Attention, Entropy, GameTheory, Fear, Habit, Abundance, Constraints, Discipline, Emergence, Mortality, Negentropy, Nothing, Procrastination, SensoryDeprivation).
- GuideReading.tsx fetches markdown and renders with react-markdown.
- vite-plugin-pwa, Supabase in stack tier list (not wired yet), Zustand store, Framer Motion, Tailwind v4.
- URL: ladiossato.github.io/deep-truth-engine/
The engine is already the superset. The merge flows toward the engine, not toward Jekyll.
The single-SPA decision
Three options were considered:
- Keep Jekyll for home + library, iframe the engine for induction. Rejected. Two URL bases, two PWAs, split install experience, split analytics. The “mandatory induction before library” rule cannot be enforced cleanly across two domains.
- Keep both, redirect home between them. Rejected. Same split-identity problem plus flicker.
- Collapse into the engine. Chosen. The engine is already a React SPA with Three.js, has the experiences wired, has a reading view, has PWA support, and has Supabase pencilled into the stack. Everything Jekyll does can be moved into React pages in a week. Nothing the engine does can be ported back to Jekyll without reinventing.
The engine becomes the new /deep-truth-guides/ site (same URL, no link decay). Jekyll repo is retired or kept as an archive of the raw markdown.
Unified flow
first visit
│
▼
Gateway (focus dot, timer, mantra fade)
│
▼
Guide Selector (16 tiles)
│ ┌──────────────┐
▼ ▼ │
Pick a guide │
│ │
▼ │
Induction (Three.js scene) │
│ │
▼ │
Reveal text │
│ │
▼ │
Reading (the guide itself) ──┘ optional; can skip back to select
│
▼
END OF FIRST-TIME PATH
────────────────────────────
return visit / subscribed user
│
▼
Home (the Parable page, prose)
│
▼
Library list (21 writings)
│
├── 3 free writings: open
├── 18 paywalled writings: pin-gated
▼
Reading or (optional) Induction for that guide
First-visit is enforced; subsequent visits are optional
On first load (no visited=true in localStorage and no user session), the app routes to the Gateway regardless of URL. The user walks Gateway → Selector → one induction → reveal → reading. At reveal we set visited=true and surface an opt-in to subscribe.
On return, the user lands on Home (the prose parable) with a small top-nav shortcut back to Gateway for anyone who wants to re-center. Library below the prose, same gate mechanic, enriched with pin-login for subscribers.
The gate, evolved
Today the gate is a single shared SHA-256 key. That served as a soft filter for casual visitors. It does not support users, tracking, or subscriptions.
New gate has two modes:
- Free tier: no login. Three writings unlocked. Other 18 tiles dimmed with a small lock glyph. Clicking a locked tile routes to the subscribe panel.
- Subscribed tier: pin + username. Unlocks all writings, plus the analytics dashboard, plus mastery tracking.
Shared SHA-256 gate is removed (or kept as a founder backdoor for demos).
The three free writings
Selected to signal depth without giving the whole arc.
- THE MACHINERY OF ATTENTION — the most universal entry point. Every reader arrives with attention damage.
- THE MACHINERY OF DISCIPLINE — the most searched folk concept, corrected at the mechanism level.
- THE MACHINERY OF NOTHING — the title that earns the brand. The reader who finishes this one either subscribes or leaves. That sorting is the point.
Rationale: Attention is the gateway drug. Discipline is the search-match. Nothing is the tuning fork. Anyone who finishes Nothing and does not subscribe is not the audience; the filter is working.
The subscription + identity model
What a user is
user {
id uuid
username text (unique, case-insensitive, claimed at subscribe)
pin_hash text (argon2id of a 4-6 digit pin; argon2id, not sha256)
email text (for recovery only; optional at v1)
subscribed_at timestamptz
subscription_tier text ('free' | 'reader' | 'founder')
stripe_customer text (nullable; only if paid)
created_at timestamptz
last_seen_at timestamptz
}
Pin is deliberately short because the threat model is not adversarial credential theft. It is “the person typing this is the one who subscribed.” Pair with device fingerprint + localStorage + rate limit and it is sufficient. If someone sophisticated steals a pin, they get a reading dashboard, not a bank account.
Auth flow
- Unsubscribed returning user clicks a locked writing → subscribe modal.
- Modal collects: username, pin, (optional) email. Stripe checkout opens in same modal (Stripe Elements). On success Supabase row is created, pin hashed, session token cookie dropped.
- Future visits: click Sign In → enter username + pin → session resumes.
Why not Supabase Auth directly
Supabase Auth assumes email/password or OAuth. The project wants a pin, not a password, because a pin fits the mono terminal aesthetic and because the value at stake is low. We use the Supabase Postgres tables directly with a thin auth edge function. Same DB, simpler UX, on-brand.
Why Stripe, not Gumroad or Patreon
One backend. One source of truth for subscription state. Clean webhook model. Native to Supabase integrations.
Tracking: the reading telemetry
Every session emits events. Nothing aggregated client-side that can be derived server-side.
Event shape
reading_event {
id uuid
user_id uuid | null (null for free-tier anonymous)
anon_id uuid (cookie-pinned, persists across sessions)
guide_slug text (e.g. 'the-machinery-of-attention')
event_type text ('view_start' | 'scroll_depth' | 'section_dwell' | 'view_end' | 'highlight' | 'share' | 'induction_complete')
payload jsonb (scroll_pct, section_id, dwell_ms, selected_text_hash, etc)
occurred_at timestamptz
client_tz text
device text ('mobile' | 'tablet' | 'desktop')
is_standalone bool (PWA installed or not)
}
Event taxonomy
| Event | Fires when | Payload |
|---|---|---|
view_start |
guide mounts | guide_slug, referrer |
scroll_depth |
on new 10% threshold crossed | pct |
section_dwell |
a heading-bounded section has been on screen >3s | section_id, dwell_ms |
view_end |
unmount, tab hide, 30s idle, or standalone close | total_visible_ms |
highlight |
user highlights text (reuses existing share.js surface) | selection_hash |
share |
user triggers share mark | selection_hash, method |
induction_complete |
a Three.js experience finishes its reveal | guide_slug, variant |
Visible-time is computed with the Page Visibility API + IntersectionObserver so background tabs do not inflate.
Why anon_id before user_id
Free-tier readers still produce signal. We want funnel visibility: how many reached Nothing, how many finished it, how many subscribed within 7 days of finishing it. Anon tracking answers that.
The analytics page
Two audiences, one surface.
Reader analytics (their own)
Every subscribed reader has a personal dashboard. Not a vanity feed. A map of how the mind in front of them is engaging with the work.
Core panels, all monochrome, all terminal-mono, all rendered as ASCII + SVG with zero rounded corners:
- The Web — force graph of all 21 machineries. Node brightness = engagement depth. Edge = sections the reader highlighted that link two machineries. Grows over time. Decays with dormancy (brightness fades when a guide hasn’t been re-read in 60 days).
- Reading timeline — horizontal strip, one tick per reading session. Thickness = duration. Hover reveals guide slug + minutes. Reads like a seismograph.
- Depth meter per guide — vertical bars, one per machinery. Fill height = cumulative minutes. Cap at an honest ceiling (say 90 min) so heavy readers don’t distort the axis.
- Section heatmap — for a selected guide, show each section’s total dwell time. The slowest-read sections are the mirror: those are the parts where the reader is working.
- Highlight corpus — every passage the reader has highlighted, grouped by machinery. Exportable as markdown. This is their own commonplace book.
- Return cadence — calendar view, like GitHub contributions but monochrome. Each cell is a reading day; intensity = minutes. Gaps are visible.
- Arc trace — the ordered sequence of guides the reader has moved through. Rendered as a thin line on a small-multiples panel. Shows whether the reader is following curated arcs or wandering.
- Induction ledger — which experiences they have completed, with timestamps. Some experiences benefit from repetition (Desire, Habit); the ledger shows whether they returned.
Innovation moves — things a standard analytics page does not do:
- Dwell pulse: real-time during a reading session, show the reader their own breath-like engagement: a small sparkline at the edge of the screen that rises on slow, attentive scroll and falls on skim. Visible feedback on the act of reading.
- Mirror quotes: the system picks, from the reader’s highlight corpus, one passage at random each visit and shows it on the dashboard with the date they first marked it. “You marked this 47 days ago. You read it again last week.”
- Decay scoreboard: across all machineries, list the guides most faded. The dashboard nudges toward the one the reader is furthest from, not the one they already know.
- Stranger mirrors: for each guide, show an anonymous silhouette of the median reader’s engagement pattern next to the reader’s own. Not a leaderboard. A silhouette. Tells the reader whether their shape is typical, deep, or odd.
- The empty panel: one panel is deliberately blank with just the text “// nothing”. Honors the machinery-of-nothing frame. Reader sees it every visit. Becomes habit.
Founder analytics (site-wide)
Same design language, aggregate scope:
- Total readers by tier, by cohort, by arrival channel.
- Funnel: land → gateway-complete → induction-complete → reading-complete → subscribe → return-7d → return-30d.
- Per-guide: unique readers, median dwell, completion rate, highlight density, subscribe-conversion lift.
- Cohort retention grid (classic retention cohort table, rendered as an ASCII-style grid).
- Top paragraphs by highlight concentration (the passages the entire readership is marking). This is writerly feedback.
- Subscribed-user churn signals: any subscriber whose last_seen_at is >45 days flagged for attention.
Founder panel lives at /dashboard and is pin-gated to founder-tier only.
The prose home, inside the SPA
The index.md parable becomes a React page component. It renders as markdown (react-markdown, same renderer as guides) so the source of truth stays in one file. At the bottom of the home, the library section renders as React with:
- Live lock/unlock state based on tier.
- Per-tile hover preview (first paragraph, dwell time if already read).
- Small
//indicator on writings the reader has not touched yet.
The gate form is replaced by the sign-in / subscribe modal.
Technical implementation plan
Phased because this is not a one-weekend job.
Phase 1: Engine becomes the site
- Point engine’s vite
baseto/deep-truth-guides/. - Mirror the 21 markdown files from deep-truth-guides into deep-truth-engine/public/guides/ (or read directly from a synced submodule).
- Port the parable into a
HomePage.tsxReact component that renders index.md content. - Add a Router or phase for ‘home’; gateway becomes a phase reachable from home, not the first-visit default.
- First-visit detection: if no localStorage
visited=true, force phase=gateway regardless of route. - Archive deep-truth-guides Jekyll or set it to redirect to engine URL.
- Deploy engine as the new canonical site. Verify PWA, install, update modal.
Phase 2: Supabase + auth
- Spin up Supabase project. Create user, subscription, event tables.
- Build Supabase edge function for pin-based auth (sign up, sign in, session).
- Replace Jekyll gate with React modal that hits Supabase.
- Implement three-free tier logic.
Phase 3: Stripe subscriptions
- Integrate Stripe Checkout + webhooks.
- Sync subscription state into Supabase on payment events.
- Gate the non-free writings on subscription tier.
Phase 4: Telemetry
- Build a lightweight client event emitter.
- Pipe events to Supabase via RPC or edge function (batched, debounced).
- Backfill nothing; start clean from go-live.
Phase 5: Reader analytics dashboard
- Implement /dashboard route, subscriber-only.
- Build panels one at a time: Web, timeline, depth bars, section heatmap, highlights, cadence, arc, induction ledger.
- Ship innovation moves (dwell pulse, mirror quotes, decay scoreboard, stranger mirrors, empty panel).
Phase 6: Founder analytics
- /dashboard/site, founder-tier only.
- Aggregate panels.
- Weekly digest email (Supabase scheduled function + transactional email).
Risks and open questions
-
URL continuity. If deep-truth-engine takes over /deep-truth-guides/, the engine’s own /deep-truth-engine/ URL is orphaned. Set a 301 redirect on engine’s GitHub Pages repo to the new location. Link rot in any external references is the cost.
-
PWA identity collision. The guides site just shipped a PWA at /deep-truth-guides/. The engine has its own PWA at /deep-truth-engine/. If the engine migrates to /deep-truth-guides/, installed users on the current PWA will see the app refresh into the new React bundle on their next cache miss. Manifest id will change. Test: install current PWA, push engine-on-guides, observe whether the app updates cleanly or the user has to reinstall. Likely the latter, which is acceptable given readership is small today.
-
“Leak analytics page” reference unclear. Ask Ladios to name a specific dashboard he considers the reference point. The innovation moves above are my best reading (mirror quotes, dwell pulse, decay scoreboard, empty panel). If he meant a specific leaked-internal-dashboard aesthetic (Twitter internal, Netflix metrics, Spotify Wrapped-style) the direction shifts.
-
Enforced first-time induction adds friction. Casual visitors who came via a shared link may bounce before experiencing anything. Mitigation: on shared-link arrivals (URL contains a fragment from share.js), show a short inline gateway (15s instead of 60s) then let them land on the shared passage. The mandatory gateway is full-length only on bare landings.
-
PIN + username at the door may be too high-friction for first subscribers. Consider: subscribe first with just email + Stripe, then on first return prompt for username and pin with the existing email+stripe_customer pairing as the recovery path.
-
Pricing is unset. Three tiers sketched (free / reader / founder). Reader tier number is the key decision. Recommended starting point: $5/month reader, free tier as above, founder tier $99/year with access to founder analytics. Easy to adjust.
-
Content-tier ambiguity. What happens to the free writings when the library grows beyond 21? Do the three stay fixed or rotate? My read: fix them for six months, then rotate quarterly. Keeps the filter working without gaming.
-
Mandatory first-time experience for subscribers on new devices. When a subscribed reader signs in from a new device, do they have to re-do the gateway? Probably no, their account carries the
has_inductedflag. But design the flag per-guide-induction so that the mastery tracking remains per-induction-attempt. -
GDPR / tracking disclosure. The telemetry is clear-eyed enough that a short privacy note is owed on the dashboard onboarding. One screen, plain language. Do it right.
-
What happens to the existing SHA-256 shared key? Today’s active readers know the word. On cutover, that word stops working. We should send a heads-up email (for those who have signed up for updates, which we do not have yet because no email capture). Alternate: keep the shared key as an override for 30 days, then retire.
Decision surface
Lock these before code starts:
- Confirm engine-becomes-site (vs Jekyll-stays-with-links-out). Default: engine becomes site.
- Confirm 3-free pick: Attention / Discipline / Nothing.
- Name the “leak analytics page” reference for the dashboard aesthetic.
- Decide pin-only vs pin+email at subscribe. Default: pin + optional email.
- Set reader-tier price. Default: $5/month.
- Confirm anon telemetry is desired for free-tier visitors (GDPR-softening). Default: yes, with a one-line disclosure at first visit.
- Decide fate of deep-truth-engine URL post-merge. Default: 301 redirect.
One-line brand update
The site’s descriptor shifts from:
Mechanistic field notes on how the mind actually works.
to:
Empty Words. Understanding nothing is everything. Induction, then map.
This pairs with the merged flow. The induction is what lets the map make sense. The machinery of nothing is the name for what crossing feels like.
Source
- Voice message 1886 (Ladios, 2026-04-18): merge sites, induction-first, three-free writings, subscribe with pin+username, per-reader tracking, innovative monochrome analytics dashboard.
- deep-truth-engine/ARCHITECTURE.md (existing induction-engine design).
- deep-truth-guides/index.md (existing parable + gate).
- THE_MACHINERY_OF_NOTHING.md (the “understanding nothing is everything” frame).