Skip to content

primer journey visualizer

Primer Learning Journey Visualizer — “Primer Print”

Section titled “Primer Learning Journey Visualizer — “Primer Print””

Created: 2026-02-27
Last updated: 2026-02-28
Status: v3 — print-ready, approved for study group distribution
Output template: primer_journey.html (single self-contained HTML, no dependencies)
Related: lessons · primer · participant feedback
Confirmed name: “Primer Print” (approved by Patrick 2026-02-28)


An interactive visualization of individual learner paths through the 79-lesson Primer curriculum. Each learner’s progression is a continuous thread weaving through lessons on a vertical spine. Reveals linear vs. spiral engagement patterns at a glance.

Core pedagogical insight made visible: Baseworks cannot be learned linearly. The expected pattern is recursive — forward into new material, then looping back to re-experience earlier lessons with accumulated understanding.

“No two Primer Prints are the same.”


  • 🔵 Theory: #4A3F8F — deep violet-indigo
  • 🟠 Key Points: #B85C1A — terracotta
  • 🔵 Practice: #2785a4 — teal

79 lesson dots, vertical, color-coded by type. Unvisited dots are faint gray.

  • Exits each dot horizontally (tangential) → smooth S-curves everywhere, no sharp peaks
  • Moving to a different lesson: thread crosses to opposite side → true S-curve
  • Amplitude is semantic:
    • Sequential step (idx+1) or natural segment boundary (e.g. 2.12→3.1): narrow (AMP_SEQUENTIAL)
    • Within-segment non-sequential revisit: medium (AMP_IN_SEGMENT)
    • Cross-segment jump: grows per segment gap (AMP_PER_SEG), capped at 580
  • Same-lesson repeat: slim vertical oval loop on current side, no side-flip
  • Multiple consecutive same-lesson repeats: nested loops, growing wider+taller, fading opacity
  • White background (#ffffff), print-optimized
  • Two columns: 292px sidebar (lesson list + visit counts) | viz area (SVG)
  • Header: Baseworks logo + title “Baseworks Primer Print” + legend
  • Footer: 6 stats + learner name (bottom right, italic serif)
  • Fully static — no scrollable containers, renders cleanly to PDF

Single HTML file. All data injected at top of <script> block. No external dependencies except Google Fonts.

const USER_NAME = ''; // e.g. "Sofia M."
const TOTAL_MINUTES = 694; // sum of Duration (min) from CSV
const LOG_RAW = [
["2026-01-16 0:47:00", "1.1-The-Problem"],
// ... one entry per lesson view: [timestamp_string, label_string]
];

Lesson matching (fuzzy — handles messy LMS labels)

Section titled “Lesson matching (fuzzy — handles messy LMS labels)”
function findLesson(label) {
// 1. Exact normName match (lowercased, punctuation normalized)
// 2. Prefix match on "X.Y" number (e.g. "3.8" matches any lesson starting with 3.8)
// 3. Partial substring match (first 12 chars)
// Returns lesson object or null
}
function normName(s) {
// lowercase, replace &#8211;/–/—/: with space, collapse whitespace
}

Amplitude constants (current values, tuned for print)

Section titled “Amplitude constants (current values, tuned for print)”
const AMP_SEQUENTIAL = 26; // narrow: sequential or seg boundary forward
const AMP_IN_SEGMENT = 52; // medium: within-segment non-sequential
const AMP_PER_SEG = 110; // per segment gap for cross-segment jumps
// max cap: 580
// State: side = +1 (right) or -1 (left); flips on every different-lesson move
// consecutiveSameCount: how many same-lesson loops drawn before a different move
// Same lesson → oval loop on current side:
// W = 28 + n*20 (width), H = 11 + n*8 (half-height), n = 0-indexed repeat
// opacity = max(0.22, 0.72 - n*0.18)
// path: M 0 y0 C ox*.5 y0-H*1.2, ox y0-H, ox y0 C ox y0+H, ox*.5 y0+H*1.2, 0 y1
// Different lesson → S-curve:
// amp = segAmp(fromLesson, toLesson, newSide)
// path: M 0 y0 C amp y0, amp y1, 0 y1 ← both CPs at x=amp → tangential entry+exit

SVG sizing (critical — controls print height)

Section titled “SVG sizing (critical — controls print height)”
const DOT_R = 6;
const STEP_Y = 20; // vertical px between lessons
const SVG_H = 78 * STEP_Y + 60; // = 1620px — always fixed
const SVG_W = 1400; // canvas half-width; arcs go ±580px from spine
// In render():
svg.setAttribute('width', '100%'); // fills column
svg.setAttribute('height', SVG_H); // fixed px
svg.setAttribute('viewBox', `-700 0 1400 ${SVG_H}`);
svg.setAttribute('preserveAspectRatio', 'xMidYMin meet');
svg.style.height = SVG_H + 'px'; // locked — prevents aspect-ratio shrinkage
svg.style.minHeight = SVG_H + 'px';
.body { grid-template-columns: 292px 1fr; } /* sidebar | viz */
.viz-area { overflow: hidden; } /* no scrollbar */
html, body { overflow-x: hidden; }

Look at the following files in the reports directory:

  • primer_journey6.html
  • generate_primer_prints.py
  • primer stats_260228.csv
First Name, Timestamp (Y-m-d H:i:s), Label, Duration (min)

One row per lesson view. Multiple users in same file, identified by First Name column.

Tell Claude Code:

Read /Users/asiahome/Obsidian/baseworks-kb-shared-brain/02-areas/primer/reports/primer-journey-visualizer.md for full project context.

Write a Python script that:

  1. Reads the CSV at [path/to/your.csv]
  2. Groups rows by First Name
  3. For each person: builds LOG_RAW from their [Timestamp, Label] rows, sums their Duration (min) for TOTAL_MINUTES
  4. Takes the HTML template at [path/to/primer_journey.html]
  5. Replaces the USER_NAME, TOTAL_MINUTES, and LOG_RAW values in the template
  6. Writes one HTML file per person to an outputs/ folder, named firstname_primer_print.html

The script does simple string replacement on these three sections in the HTML:

const USER_NAME = '';
const TOTAL_MINUTES = 694;
const LOG_RAW = [ ... ]; // entire array, from opening [ to closing ];

Use regex to replace the LOG_RAW block:

import re
log_raw_str = repr(list_of_tuples) # or build JS array string manually
content = re.sub(r'const LOG_RAW = \[.*?\];', f'const LOG_RAW = {log_raw_str};', content, flags=re.DOTALL)
content = content.replace("const USER_NAME = '';", f"const USER_NAME = '{name}';")
content = content.replace("const TOTAL_MINUTES = 694;", f"const TOTAL_MINUTES = {total_minutes};")

  • ✅ Static HTML/SVG, single file, no dependencies
  • ✅ 79-lesson spine, type-colored dots (indigo/terracotta/teal)
  • ✅ Option F geometry: tangential S-curves, nested loops, segment-proportional arcs
  • ✅ Smart amplitude: sequential/boundary moves = narrow arc
  • ✅ Gradient coloring per segment
  • ✅ Sidebar: truncated names + colored ×N counts
  • ✅ Footer: views · revisits · unique · ratio · hours · days · learner name
  • ✅ Baseworks logo (inline SVG from BW LOGO SVG 90.svg)
  • ✅ White background, print-optimized
  • ✅ No scrollbars — fully static layout
  • ✅ Height locked at 1620px (matches full 79-lesson sidebar)
  • ✅ Text sized for US Letter print
  • ✅ Tested with real user data (Jan 16 – Feb 27 2026, segments 1.1–7.9)

  • Animation: draw thread chronologically (replay button) — the social share moment
  • PDF/print button in UI
  • Segment band labels on spine (subtle chapter markers)
  • Learner archetype detection (linear / spiral / cluster / free ranger)
  • Correlate visit patterns with segment_feedback.csv scores
  • Population aggregate view: line thickness = transition frequency across users
  • N8N automation: course completion → generate → deliver

  • Do deeper revisitors report more transformation in feedback?
  • Critical junctures where learners tend to revisit across the population?
  • Early navigation patterns as predictor of engagement depth?
  • Connects to: affordance competition theory, motor learning stages, communicability framework