Skip to content

Technical documentationInternal

For the team and anyone taking the product over. Not linked from the site and not indexed.

Technical documentation pages

Education tools

The Education tools are fourteen independent teaching workspaces, each at its own /<id>/app address, that pair a short interactive experiment (a sequence, a diagram, a chart or a calculation) with an explanation, sources and, for ten of them, an experimental PDB structure shown as a Blender still with an optional Mol* 3D view and, for three, a short guide video. This page describes how they are routed and rendered, how their models and media are produced, how their science is checked, and what is incomplete.

Purpose and users

docs/design/education-workspaces.md (7 October 2026) names the audience as university students and early-career bioscience learners who want to explore a mechanism or refresh their understanding. Each tool states a question, runs a bounded experiment, and ends with an "At the bench" link into a suite app. content/education.ts opens with the rule the tools follow: they are schematic experiments, and their sources support the biology, not a claim of predictive simulation.

The tools are not registry apps. They have no entry in content/apps.ts, no product page, docs page, changelog, version, status, sourceMeta or dataPolicy, and no names in content/app-names.ts. Navigation, routing, security headers and the landing page read a separate small registry, content/education-tools.ts. Cell (inside-a-cell, see Cell) is a registry app that the Education navigation groups alongside them.

The roadmap (docs/roadmap.md) lists, as unfinished work, a review of all fourteen tools by the CSO (models, parameters, claims), linking them from the apps they teach, and cutting those that do not earn their place.

Addresses

Address Tool (EDUCATION_NAV title) Presentation
/mutation/app Mutation Lab Molecular view + experiment
/replication/app DNA Replication Molecular view (with guide video) + experiment
/splicing/app RNA Splicing Molecular view (with guide video) + experiment
/pcr/app PCR Lab Molecular view + experiment
/atp-synthase/app ATP Synthase Molecular view (with guide video) + experiment
/gene-switches/app Gene Switches Molecular view + experiment
/crispr/app CRISPR and Repair Molecular view + experiment
/rna-shape/app RNA Shape and Function Molecular view + experiment
/protein-structure/app Protein Structure Molecular view + experiment
/binding-inhibition/app Binding and Inhibition Molecular view + experiment
/dose-response/app Dose–Response Interactive (experiment is the stage)
/dilutions-buffers/app Dilutions and Buffers Interactive
/molecule-properties/app Molecule Properties Interactive
/growth-oxygen/app Growth and Oxygen Interactive

educationToolHref(id) in content/apps/routes.ts builds the address (/<encodeURIComponent(id)>/app). Each address is a static route file, src/app/(workspace)/(education-tools)/<id>/app/page.tsx, of the form:

export const metadata = educationMetadata("<id>");
export default function Page() { return <EducationEntry id="<id>" />; }

The pages sit in the (workspace) route group, so they inherit its robots: { index: false } and no-analytics rules (see Architecture), and in the (education-tools) sub-group, whose layout.tsx adds ThemeScript for the suite theme (slug="suite", light and dark) and colorScheme: "light dark". There are no sub-pages and no URL state: nothing is read from or written to the query string or the #s= fragment. Each workspace page is its own document-policy area, so following a link between two tools is a full document load (see Security and network).

The tool ids share the top-level path namespace with the app segments (protein, molecule, pipeline, cell, calculators, sequence, assay, plasmid, bioreactor) and reserved words. No test checks that a new Education id cannot collide with a segment.

File map

Path Role
content/education-tools.ts EDUCATION_NAV (14 entries: id, title, label), EducationToolId, EDUCATION_CATEGORIES (six subject groups, including inside-a-cell)
content/education.ts EducationTool type; EDUCATION_TOOLS (the eight molecular-biology tools inline, then the structural, quantitative and applied lists); educationToolFor(id)
content/education-structural.ts STRUCTURAL_TOOLS: protein-structure, binding-inhibition
content/education-quantitative.ts QUANTITATIVE_TOOLS: dose-response, dilutions-buffers (presentation: "interactive")
content/education-applied.ts APPLIED_TOOLS: molecule-properties, growth-oxygen (presentation: "interactive")
content/education-ui.ts educationUi.interactiveDisclosure (the only workspace string held in content)
content/education.test.ts Registry, route, header and media-manifest consistency tests
content/apps/landing-education.ts The landing page's Education section (featured tools, groups, a dose plot), exported in /landing.json
src/app/(workspace)/(education-tools)/layout.tsx Theme script and viewport for the group
src/app/(workspace)/(education-tools)/<id>/app/page.tsx Fourteen route files
src/apps/education/entry.tsx educationMetadata(id) (title, description, canonical path, share card) and EducationEntry
src/apps/education/workspace.tsx EducationWorkspace, MolecularView, SelfCheck; lazy map of the fourteen experiments
src/apps/education/education.css Layout grid, stage size, disclosure borders, diagram classes, reduced-motion and print rules
src/apps/education/experiments/diagram-palette.ts useDiagramPalette(): diagram colours from the suite chart theme
src/apps/education/experiments/sequence/ model.ts (mutation, PCR, CRISPR), mutation-lab.tsx, pcr-lab.tsx, crispr-lab.tsx, experiments.module.css, model.test.ts
src/apps/education/experiments/nucleic/ models.ts (replication, splicing, RNA hairpin), controls.tsx (useModelTimeline, TimelineControls), the three labs, models.test.ts
src/apps/education/experiments/machines/ models.ts (ATP synthesis, lac regulation), cycle.tsx (useCycle, CycleControls), atp-lab.tsx, gene-switches-lab.tsx, models.test.ts
src/apps/education/experiments/structural/ models.ts (binding fractions, trace projection), protein-structure-lab.tsx, binding-inhibition-lab.tsx, models.test.ts
src/apps/education/experiments/quantitative/ models.ts (4PL samples, serial dilution, buffer balance), controls.tsx (RangeControl, number), the two labs, quantitative.module.css, models.test.ts
src/apps/education/experiments/applied/ molecule-model.ts, molecules.json, build-molecules.mjs, growth-model.ts, the two labs, models.test.ts
src/components/suite/education-mark.tsx One mark per tool id, used in the frame identity and navigation
pipeline/education/ molecular.py, verify.py, encode_webm.py, catalog.json, replication-proof.json, requirements.txt, README.md
pipeline/education/structure-sources/ prepare.py, assembly.py, build_views.py, render-selections.json, cell-selections.json, sources.json, metadata/*.metadata.json, README.md; coordinates/ is a git-ignored download cache
public/data/education/ manifest.json, scenes.json, ten *-structure.webp posters, three *-guide.mp4 and three *-guide.webm
public/data/education/structures/ Ten *-view.bcif.gz, their *-view.metadata.json, views.json, index.json, 1ubq-residues.json
e2e/education/ Eight browser test files (see Tests)
docs/design/education-workspaces.md Current design record
docs/design/education-expansion-research.md Research basis for the first eight tools (7 October 2026)

UI structure and state

Frame

EducationWorkspace renders inside SuiteFrame (src/components/suite/objects/suite-frame.tsx), the frame used by the Bench, object, study and Literature pages, with accent="inside-a-cell" (every Education tool uses Cell's accent) and an identity of the tool's title and EducationMark. The frame supplies the top bar, the Research sidebar (from lg a rail whose Learn tile, current on a tool's page, opens the full list; there and in the menu sheet the Learn group lists EDUCATION_CATEGORIES, opening the current subject), the menu sheet, the status bar and command-palette entries ("Switch to <title>") for each tool. See Shared shell.

Page layout

workspace.tsx renders an article (data-education-tool=<id>):

  1. A header with the title (shared subjectHeading) behind the 4 px accent bar, the description, and under them the in-page links to #experiment, #reference and #sources drawn as underline tabs on a hairline across the column (D-0084; they remain links in a nav).
  2. An overview grid (two columns once the main container is at least 840 px):
    • for the four interactive tools, the experiment itself (section#experiment); for the other ten, MolecularView with the poster, optional video and optional Mol* view;
    • an aside with the topic kicker, the tool's question, and either "What to explore" (objectives, interactive tools) or "What you're looking at" (the media purpose, a colour legend from the manifest and a closed "Structure details" disclosure with the media limitations).
  3. For the ten molecular tools, the experiment in its own section#experiment below the overview.
  4. A support grid with a closed "Quick reference" disclosure (the three refresher paragraphs), an optional "Check your understanding" disclosure (SelfCheck), the "At the bench" card (connection.why and a link), and a closed "Sources and model notes" disclosure (the manifest disclosure or educationUi.interactiveDisclosure, then every sources entry).

Each experiment component is loaded with next/dynamic (server rendering left on, a "Loading the experiment…" placeholder). The experiment stays mounted while disclosures open and close, so its state survives reading the reference material.

State

All state is React component state (useState, useMemo, refs). There is no persistence, no URL state, no export, no Study or object integration, and no server call. Reloading a page resets the experiment and the self-check. SelfCheck records nothing; it shows the chosen answer's feedback with "That’s right." or "Take another look." in a polite live region.

Shared timing helpers:

Helper File Behaviour
useModelTimeline(lastStep, interval = 2300) nucleic/controls.tsx Finite timeline, paused on opening. Play advances one step per interval; under reduced motion play is inert and the step controls remain. Used by replication, splicing, RNA shape and growth (48 steps at 220 ms).
useCycle(count) machines/cycle.tsx Same pattern with a 1,350 ms step; Play is disabled under reduced motion. Used by ATP synthase and gene switches.
Mutation Lab interval sequence/mutation-lab.tsx Reads one codon every 1,050 ms after Play, up to eight codons; Pause, step and reset remain.

Reduced motion is read from src/components/suite/motion/use-reduced-motion.ts; education.css also turns off .edu-motion transitions under prefers-reduced-motion: reduce and html[data-motion="reduce"].

Diagram colours come from useDiagramPalette(), which maps dna, mrna, trna, protein and ligand onto slots of the suite chart theme (useChartTheme), so diagrams follow light and dark. The molecular posters and the Mol* category colours are separate and fixed (Cell's palette).

Molecular view

MolecularView has three views: still (default), guide and 3d.

  • Still. An <img> of the poster with ?v=<manifest.version> appended (assetUrl), fetchPriority="high", inside the shared Stage (mode="locked" for the still and video, mode="theme" for 3D). A bar at the bottom offers "Load the 3D view" (or "Return to 3D view") and, where a guide exists, "Play molecular view".
  • Guide. A muted, playsInline, preload="none" <video> with WebM then MP4 sources. Playback starts only on request; controls appear once started; a play failure shows a status message and keeps the still.
  • 3D. The first request mounts MolstarViewer (the default export of src/components/tools/inside-a-cell/molstar-viewer.tsx, loaded with next/dynamic, ssr: false) with scenesUrl = /data/education/scenes.json?v=<version>, stepId = media.sceneId and pdbId = media.pdbId. Once mounted it is kept, hidden with inert and aria-hidden, when the reader returns to the still or video, so returning to 3D does not download again (e2e/education/structure.spec.ts checks the structure request count).

Under the stage, the manifest's references render as external links ("PDB <id>" for RCSB structure links, otherwise "Source <n>").

3D viewer reuse

The Education tools run Cell's viewer unchanged; everything in Cell's 3D viewer applies: WebGL check on request, vendored Mol* 5.11.0 from /vendor/molstar@5.11.0/, one MolViewSpec scene per load, gzipped BinaryCIF downloaded by the page with idle and total timeouts, software-rasteriser props, theme-following canvas, Stage zoom and reset, and touch "Rotate". The viewer's status and error strings are Cell's (content/apps/inside-a-cell.workspace.ts), so messages such as the no-WebGL fallback refer to "a still rendered in Blender from the same structure". The viewer's key is the scenes URL, so Cell and Education never share a scene map. molstar-viewer.tsx declares the scenesUrl prop for this purpose.

The scene file must satisfy Cell's SceneFile type (src/components/tools/inside-a-cell/types.ts). Two optional SceneGroup fields exist for Education: select (residue-level selectors in place of whole entities) and colorRegions (recolour parts of one continuous cartoon). mvs.test.ts covers both.

Experiments and their models

Every model is a pure TypeScript module with a Node test. The tables give what the code computes; quoted limitations are in the tools' own text.

Sequence experiments (experiments/sequence/model.ts)

Tool Model
Mutation Lab ORIGINAL_DNA = "ATGGCTGAATGGCCTAAGGGTTAA" (synthetic, standard code). Four fixed cases: synonymous (base 6 T→C, GCU→GCC), missense (base 8 A→T, E→V), premature stop (base 11 G→A, UGG→UAG), frameshift (delete base 7). codonsOf splits into codons, translates with src/lib/seq/translate.ts, and marks codons after the first stop as untranslated; peptideOf translates to the stop. Peptides: MAEWPKG, MAEWPKG, MAVWPKG, MAE, MANGLRV.
PCR Lab PCR_TEMPLATE (36 nt), 8-nt primers (PCR_FORWARD = first 8 nt; PCR_REVERSE = reverse complement of the last 8). Primer case inward, outward or mismatch. boundedPcrProducts(n, case) = 2^n - 2n exact-length duplexes for inward (0 otherwise), n clamped to 0 to 8 (PCR_MAX_CYCLES). pcrProgress(step) maps 0 to 24 steps onto denature, anneal and extend phases, completing a cycle only after extension.
CRISPR and Repair CRISPR_TARGET (20 nt) + PAM TGG. Guide cases match, mismatch (spacer differs at the PAM-proximal end) and no-pam (PAM TCT); only match can demonstrate a cut. repairedTarget: unchanged, deletion (removes base 17 of the protospacer), donor (only with a donor: base 17 → A and PAM TGG → TGC). The code and text call these "curated repair illustrations, deliberately not a probability or efficacy model".

Nucleic-acid experiments (experiments/nucleic/models.ts)

Tool Model
DNA Replication Five phases (REPLICATION_STEPS). replicationState(step, ligase) returns fragment count (1 then 3), primers removed from phase 3, joined only at phase 4 with ligase, nicks = fragments - 1 when primers are removed and the backbone is not joined, both strands 5′ → 3′, and a status sentence per phase. Described in code as "a directionality/maturation model, not a kinetic replication simulation".
RNA Splicing Three synthetic exons (AUGGCU, GAACCA, UUCGGAUAA) and a 65-nt intron (GUAUAA + 52 C + CUAACAG). spliceProduct(choice) for include, skip or retain returns the sequence, length change, downstream frame shift ((delta mod 3) + 3) mod 3, peptide to the stop, stop-codon position and a consequence sentence. Include: MAEPFG, stop at codon 7. Skip: MAFG, delta −6, frame preserved. Retain intron 1: MAV, stop at codon 4, shift 2.
RNA Shape and Function 14-nt hairpin GCAUGUUCGCAUGC, stem pairs (0,13) … (4,9), loop UUCG. Variants: original, disrupted (A3→C, C–U mismatch), rescued (U12→G, C–G). hairpinModel counts canonical pairs (AU, UA, GC, CG; G–U wobble is not counted as canonical).

Molecular machines (experiments/machines/models.ts)

Tool Model
ATP Synthase Qualitative. Drive is none, weak or strong; a leak lowers it one level. Synthesis needs drive above none and substrates (ADP and phosphate). Status strings: "ADP and phosphate needed", "No synthesis drive", "Reduced synthesis drive", "Synthesis supported". No rates.
Gene Switches regulationState(inducer, repressorPresent): the repressor is bound when present and no inducer; transcription is then repressed, otherwise permitted. Glucose regulation is held fixed and stated as omitted.

Structural experiments (experiments/structural/models.ts)

Binding and Inhibition: a one-site equilibrium with mutually exclusive ligand and competitor at prescribed free concentrations (depletion omitted), set on logarithmic sliders from 0.01 to 1,000 nM (defaults: ligand 10 nM, Kd 10 nM, competitor 20 nM, Ki 10 nM).

l = [L]/Kd,  i = [I]/Ki,  D = 1 + l + i
fraction ligand-bound      = l / D
fraction competitor-bound  = i / D
fraction free              = 1 / D
baseline (no competitor)   = l / (1 + l)
competition midpoint       = Ki * (1 + [L]/Kd)        (Cheng–Prusoff form)

Inputs must be finite, concentrations non-negative and Kd, Ki positive, or RangeError. The text states the constants are illustrative and not taken from PDB 3PTB.

Protein Structure: reads public/data/education/structures/1ubq-residues.json (imported at build time): 76 Cα positions of PDB 1UBQ chain A, the sequence, deposited isotropic B factors (Ų), and features from the deposited _struct_conf and _struct_sheet_range records: helix 23–34, 3₁₀ helix 56–59, sheet strands 1–7, 10–17, 40–45, 48–50, 64–72, and an authored "C-terminal region" 72–76. projectTrace(residues, yaw, pitch) is an orthographic rotation of the deposited coordinates about their centroid, scaled into a 720 × 408 viewBox; it never alters the conformation. The disorder view colours residues by B factor (thresholds 10 and 20 Ų).

Quantitative experiments (experiments/quantitative/models.ts)

Dose–Response reuses the suite's 4PL equation, fourPL in src/lib/fit/curve.ts:

y(x) = bottom + (top - bottom) / (1 + 10^((logC50 - x) * h)),   x = log10(concentration in mol/L)
h = +hill for "increasing", -hill for "decreasing"
defaults: bottom 10, top 90, logC50 -8, hill 1, logMin -10, logMax -6, noise 4, seed 23

doseSamples(model, levels = 9, replicates = 3) spaces nine log levels evenly and adds normal errors by Box–Muller from a seeded linear congruential generator (state = state * 1664525 + 1013904223 mod 2^32), never clipped. doseCoverage reports whether the range brackets C50 and whether both ends reach within 10% of each plateau; the text calls the 10% threshold illustrative. No regression is performed.

Dilutions and Buffers has two tabs:

serial dilution (stock µM, transfer µL, diluent µL, 1-8 tubes):
  each tube: C2 = C1 * V1 / (V1 + Vdiluent)     via solveC1V1 (src/lib/dilution/c1v1.ts)
  retained volume = diluent for all but the last tube, transfer + diluent for the last
  pmol = µM * µL

acetate buffer (total mM, volume mL, log10(A-/HA), strong acid or base titrant mM, added mL):
  convert min(added mmol, reserve) of one partner into the other
  exhausted when added >= reserve; pH then withheld (null)
  pH = pKa + log10(nA- / nHA),  pKa = 4.756 (bufferById("acetate"), src/lib/dilution/buffer.ts)
  beta = ln(10) * C_total * f_HA * f_A-   (mM per pH unit; water and activity omitted)

The pKa is the same table value Calc uses; the source line cites Goldberg, Kishore and Lennen (NIST) "Table 8".

Applied experiments (experiments/applied/)

Molecule Properties compares benzene, phenol and anisole. build-molecules.mjs runs the installed @rdkit/rdkit through Molecule's own descriptorsOf, parseSmiles and drawSvg (src/components/tools/molecule-workbench/chem.ts) and writes molecules.json: RDKit version (2026.03.6), method note, and for each molecule its SMILES, PubChem CID, formula, mw, hbd, hba, tpsa, clogp, and four SVG depictions (light and dark, with and without the oxygen highlighted). The lab shows the dark SVGs as data: image URLs and computes propertyDifference and partitionRatio = 10^(clogP_b - clogP_a). The values are precomputed; the page does not load RDKit.

Growth and Oxygen is a fixed-volume batch culture with two Monod terms:

mu = mu_max * S/(Ks + S) * O/(Ko + O)
dO/dt (transfer) = kLa * (O* - O)
growth consumes S at 1/Yxs and O at 1/Yxo per unit biomass
mu_max 0.5 /h, Ks 0.08 g/L, Ko 0.2 mg/L, O* 8 mg/L, Yxs 0.5 g/g, Yxo 0.8 g/g, X0 0.12 g/L
scenarios: "nutrient" (S0 0.5 g/L, kLa 80 /h) and "oxygen" (S0 4 g/L, kLa 8 /h)
intervention at 6 h: nutrient pulse 1.5 g/L (negligible volume) and/or kLa 80 -> 160 or 8 -> 80
12 h, output every 0.25 h, default 2,000 integration steps per hour

simulateGrowth relaxes oxygen towards saturation analytically over each step ((O* - O) * -expm1(-kLa dt)), then solves an implicit growth increment by 24 bisection steps bounded by the available nutrient and oxygen, which keeps the inventories non-negative and both yield balances closed. The dashed curve is the same scenario without intervention.

Authored models and media

Pipeline overview

Script Reads Writes
structure-sources/prepare.py https://models.rcsb.org/<id>.bcif for the ten entries; Cell's scenes.json coordinates/<id>.bcif (ignored cache), metadata/<id>.metadata.json (source URL, SHA-256, byte and model-1 atom counts, entity and chain inventory with label and author ids, revision, assembly generation, polymers), sources.json, cell-selections.json (Cell's translation and splicing scenes)
structure-sources/assembly.py coordinates/1efa.bcif coordinates/1efa-assembly1.bcif (atom rows of assembly 1 only, identity operator asserted, chain E absent), metadata/1efa-assembly1.metadata.json
structure-sources/build_views.py the cached originals; Cell's scenes.json public/data/education/structures/<id>-view.bcif.gz, <id>-view.metadata.json, views.json, index.json, 1ubq-residues.json
molecular.py render-selections.json, views.json, catalog.json, replication-proof.json, Cell's scenes.json and render.py public/data/education/scenes.json, manifest.json, posters (--stills), guide videos (--guided)
encode_webm.py the three MP4 guides VP9 WebM alternates
verify.py the published media and scenes Assertions only

The rebuild sequence and Python 3.13 environment are in pipeline/education/README.md. Dependencies (requirements.txt): bpy 5.2.2, molecularnodes 520.2.0, nodebpy 520.27.0 (pinned because 520.33.0 removed a helper Molecular Nodes imports), biotite 1.7.1, numpy 2.5.3, scipy 1.18.1, imageio-ffmpeg 0.6.0, Pillow 12.3.0. None of this runs in CI. See Data pipeline.

Structures

Tool PDB Revision Served file Atoms gz bytes Trimming
mutation 6Y0G 1.2 (2024-05-22) 6y0g-view.bcif.gz 20,568 214,906 Byte copy of Cell's 6y0g-lite.bcif.gz
replication 7PFO 1.1 (2025-07-09) 7pfo-view.bcif.gz 7,077 94,455 Selected protein entities as CA; DNA entities 17 and 18 and ligand chains W, X, Y, Z, CA, DA at full heavy atoms; Pol ε entities 7 and 8 and AND-1 entity 14 omitted
splicing 6QDV 3.1 (2024-11-13) 6qdv-view.bcif.gz 21,974 212,203 Byte copy of Cell's 6qdv-lite.bcif.gz
pcr 3KTQ 1.3 (2024-02-21) 3ktq-view.bcif.gz 4,725 47,504 Full heavy atoms, no water
atp-synthase 6OQR 1.1 (2025-06-04) 6oqr-view.bcif.gz 5,033 55,388 All protein as CA; ligands at full heavy atoms
gene-switches 1EFA 2.2 (2024-02-07) 1efa-view.bcif.gz 5,611 50,407 Assembly 1 rows; full heavy atoms
crispr 4UN3 1.3 (2024-05-08) 4un3-view.bcif.gz 13,224 121,934 Full heavy atoms
rna-shape 1EHZ 1.4 (2022-12-21) 1ehz-view.bcif.gz 1,661 19,571 Full heavy atoms
protein-structure 1UBQ 1.3 (2024-02-14) 1ubq-view.bcif.gz 602 10,110 Full heavy atoms
binding-inhibition 3PTB 1.4 (2024-11-20) 3ptb-view.bcif.gz 1,639 19,561 Full heavy atoms

build_views.py keeps the same mmCIF categories as Cell's pipeline plus struct_conn, drops hydrogen and water, and gzips at level 9 with mtime=0. Each *-view.metadata.json records the selection rule, served SHA-256 and the SHA-256 of the source file. The originals were retrieved on 7 October 2026 (retrievedUtc in each metadata file). Chain selection uses label_asym_id and label_entity_id throughout (render-selections.json, selectionConvention).

Scene file

molecular.py prepare() builds public/data/education/scenes.json for the ten tools:

  • mutation, splicing: deep copies of Cell's translation and splicing scenes with the id changed and each group's palette label filled in.
  • replication: the hand-authored record in replication-proof.json (groups, ligands, focus, cutaway), with group labels set, the camera rolled −21° and the radius set to 106 Å.
  • the other seven: groups from render-selections.json mapped onto Cell's colour categories by a fixed per-tool list (category_maps), with residue select and colorRegions carried over, and a camera from auto_camera: direction from the nucleic-acid centroid towards the protein centroid where both exist (tool-specific rules for gene-switches, protein-structure and binding-inhibition), a cutaway plane for pcr, crispr and binding-inhibition, a radius from the 99.5th percentile (or maximum) distance, and a 14° roll (none for gene-switches).
  • Labels (labels_for) are placed in the left and right margins of the view at the height of their atoms, at most four, with tool-specific names; they are not anchored on the atoms as Cell's are.
  • pdbRevision, url, format, lite, atoms, fullAtomEntities and structure come from views.json; sizeNm is computed from the convex hull where missing.

Because the categories are reused for meaning rather than molecule type, an Education colour does not always mean what it means in Cell. Examples: the lac repressor is drawn in the srna violet, the PCR primer in the trna pink, the ATP synthase rotor in the newchain coral, the 3PTB pocket residues in the mrna mint, and 7PFO's lagging-strand template in the mrna mint. The manifest's legend (colour word → meaning) is what the page shows, and the scene group label is what the 3D tooltip shows. workspace.tsx maps the colour words to Cell's palette (molecularColours).

scenes.json carries retrieved: "2026-10-07", written as a literal by molecular.py, not the run date.

Posters and guide videos

molecular.py build() reuses Cell's render.py (imported by path) for world set-up, trace positions, camera placement and colour conversion. Posters are 1600 × 900, 32 samples by default, 2 threads, max_bounces = 4, WebP quality 90. Five tools (pcr, crispr, gene-switches, protein-structure, binding-inhibition) get an extra soft area light. Cartoon or ribbon is chosen per group by whether it has CA atoms; spacefill groups and ligands are spheres.

render_guided() renders three highlight states for replication, splicing and ATP synthase at 1280 wide: all colours dimmed to 34%, the selected atoms brightened by 1.55 (capped at 1), a caption and a "PDB <id> / FIXED COORDINATES + AUTHORED HIGHLIGHTS" footer rendered in Blender. FFmpeg (from imageio-ffmpeg) crossfades the three frames (3 s each, 1 s fades) into a 7 s, 24 fps H.264 yuv420p MP4 with +faststart, and encode_webm makes a VP9 WebM (CRF 28). The coordinates never move; the README and manifest state that the videos are not molecular trajectories.

publish_manifest(include_videos) copies catalog.json to public/data/education/manifest.json, adding video and videoWebm links only when --publish-videos is passed and all three clips exist. The published manifest equals catalog.json apart from those six links (checked for this page on 10 October 2026).

Media sizes on disk: posters 18,822 to 77,600 bytes; MP4 guides 164,728 to 249,123 bytes; WebM guides 144,555 to 186,137 bytes; structure files 10,110 to 214,906 bytes.

Manifest shape

{ version: 4, created: "2026-10-07", generator, renderer, provenance, disclosure,
  posterDimensions: [1600, 900], videoDimensions: [1280, 720],
  videoFramesPerSecond: 24, videoDurationSeconds: 7,
  assets: [ { id, poster, alt, purpose, limitations, legend: {colour word: meaning},
              references: [url...], sceneId, pdbId, structure, video?, videoWebm? } ] }

The workspace imports the manifest at build time (import mediaManifest from "../../../public/data/education/manifest.json"), and so does content/apps/landing-education.ts, which puts the featured tools' poster, alt text, dimensions and first reference into /landing.json.

Licences

RCSB PDB coordinates are CC0. Blender and Molecular Nodes run only at build time; the pipeline README states that their GPL licences do not apply to the rendered outputs. Whether that holds for sale is held for the General Counsel. Mol* is MIT. RDKit is used through the existing @rdkit/rdkit dependency; see Dependencies and licences. The non-structural sources (NHGRI, NCBI, HHMI BioInteractive, CSHL DNA Learning Center, Utah Learn.Genetics, OpenStax, the NIST reprint, the NCBI Assay Guidance Manual, journal articles, RDKit documentation) are links for further reading; no licence id or read date is recorded for them because the tools are outside the registry's sourceMeta.

Science content and how its accuracy is checked

What each tool claims and cites

Each EducationTool holds a description, a question, three objectives, three refresher paragraphs, an "At the bench" connection, one to three sources (label, URL, detail), and a three-choice checkpoint with feedback per choice and the index of the right answer. The ten molecular tools also have the manifest's purpose, limitations and legend. The limitations record what the structure does not show, for example: 6Y0G's synthetic poly(U) message, bacterial-sequence tRNAs and cycloheximide; 7PFO's unresolved DNA segment between residues 46 and 51; 3KTQ's dideoxy primer terminus and ddCTP; 1EFA's engineered A109T dimer with the anti-inducer ONPF omitted; 4UN3's H840A Cas9 and 12-residue non-target fragment; 1UBQ's B factors not being prediction confidence; 3PTB's pose not determining Kd, Ki or IC50.

Automated checks

Check What it verifies
content/education.test.ts 14 unique ids; EDUCATION_TOOLS in EDUCATION_NAV order; categories cover each tool once plus inside-a-cell; each educationToolHref is /<id>/app with a route file; each has a CSP header rule containing default-src 'self'; four interactive tools; ten manifest assets; every non-interactive tool has a poster file, alt text and limitations longer than 30 characters, and references; every tool has sources and an /app connection; every connection opens an existing app workspace with no #s= fragment, and a link that carries query state (Cell step, Sequence op) uses only keys and values the receiving app's address schema (src/apps/inside-a-cell/url-schema.ts, src/components/tools/sequence-toolkit/address.ts) accepts
sequence/model.test.ts The four mutation peptides; codon flags after a stop and for an incomplete codon; 2^n - 2n for cycles 0 to 5 and the 8-cycle cap (240); outward and mismatch give 0; primers match the template ends; PCR phase counting; CRISPR guide outcomes and donor dependence
nucleic/models.test.ts Ligase only matters after primer replacement; two nicks without ligase; both strands 5′→3′; the three splice products, lengths, frame shifts and stop positions; the intron is 65 nt and GU…AG; hairpin pair counts and the preserved UUCG loop
machines/models.test.ts Synthesis needs drive and substrates; a leak lowers drive and cannot create it
structural/models.test.ts Half occupancy at [L] = Kd; site fractions sum to 1; competition midpoint Ki(1 + [L]/Kd) = 20 for the test case; invalid input throws; the 76-residue trace keeps sequence order under rotation and stays inside the viewBox; the helix has 12 residues; residues 73 to 76 have B factors above 30
quantitative/models.test.ts Response at C50 is the plateau midpoint in both directions; coverage flags; seeded noise is reproducible and does not move the prescribed curve; serial dilution 100, 10, 1 µM with solute and volume conserved; buffer mole bookkeeping, symmetric acid and base, capacity falling with addition and with dilution; exhaustion returns pH = null with explicit excess
applied/models.test.ts molecules.json equals a fresh RDKit calculation (same RDKit version, formula, and each descriptor within 1e-9); phenol → anisole loses one donor, keeps acceptors, gains 14.027 g/mol and has a partition ratio of about 2.009; every growth run keeps inventories non-negative and both yield balances closed to 1e-9; feeding rescues the nutrient case but not the oxygen case and vice versa for aeration; the pre-intervention trajectory is unchanged by the intervention; 2,000 and 4,000 steps per hour agree within 0.001 g/L biomass and 0.2 DO percentage points
pipeline/education/verify.py (by hand) Manifest version ≥ 4; ten assets and ten scenes; three MP4 and three WebM; each poster is WebP at 1600 × 900; each scene's PDB id matches the asset and its structure file unzips to more than 1,000 bytes; each video's codec (h264 or vp9), size, 7 s duration, 24 fps and frame count; referenced media under 12,000,000 bytes; the ubiquitin trace (76 residues, sequence, Cα, non-negative B, helix and sheet ranges)

On 10 October 2026, node --test src/apps/education/experiments/*/*.test.ts content/education.test.ts ran 27 tests, all passing. verify.py is not run by npm test or CI (see Data pipeline).

There is no independent oracle for these models beyond the RDKit re-calculation and the conservation and refinement checks above. The 10 October 2026 scientific audit (docs/science/2026-10-10-scientific-audit-of-the-7-to-10-october-changes.md) reports all fourteen models correct, twelve PDB entries confirmed against the RCSB API, the RDKit descriptors matching, and the competitive-binding algebra and buffer bookkeeping correct. The same audit lists open findings (see Known gaps).

The PDB revisions, citations and entity descriptions are not re-verified by an automated script as Cell's are; prepare.py records revision and checksum, and the per-tool limitations in render-selections.json were written by hand.

Integration with the suite

  • Navigation. research-navigation.tsx renders EDUCATION_CATEGORIES in the full list's Learn group (the lg overlay that the rail's Learn tile opens, and the menu sheet); the rail itself shows Cell's row and the Learn row (the Apps switcher was removed by D-0082, 13 October 2026); suite-frame.tsx adds a command-palette entry per tool. EDUCATION_CATEGORIES places Cell in "Cell biology & energy".
  • Headers and policy. suiteHeaders in content/apps/next-config.ts applies headersFor("inside-a-cell") to /<id>/app/:path* for every EDUCATION_NAV entry, so the Education pages get Cell's Content Security Policy: no external hosts, plus 'unsafe-eval' and data: in connect-src for Mol*. content/csp.test.ts checks these rules. See Security and network.
  • Caching. Posters, videos, scenes and structures sit under /data/* and get the suite's data cache rule. Posters, videos and the scene file are requested with ?v=<manifest.version>; the structure URLs inside scenes.json are not versioned.
  • Share cards and metadata. educationMetadata(id) uses cardForPath and cardMetadata (src/lib/sharing/), which build a card from the tool's title, topic, description, question and refresher titles; the canonical path is the tool's address.
  • At the bench links. Each tool's connection.href is built with appHref: Sequence (mutation, replication, crispr; pcr opens ?op=primer-check), Cell (splicing → ?step=splicing; rna-shape → ?step=translation), Protein (atp-synthase, protein-structure), Plasmid (gene-switches), Assay (binding-inhibition, dose-response), Calc (dilutions-buffers), Molecule (molecule-properties), Bioreactor (growth-oxygen). These are plain links; nothing is handed off. A replacement card added on 17 October 2026 (named presets that no app defines, links to /reference/workflows/* routes that do not exist, and an LMS iframe generator that the education pages' frame-ancestors 'none' policy blocks) was removed the same day; this card is the only bench link.
  • Shared code. The tools import Cell's viewer and palette, Sequence's translate and revcomp, Assay's fourPL, Calc's solveC1V1 and buffer table, Molecule's RDKit helpers (build script and test only), and Cell's render.py (pipeline). A change to any of these can change an Education result; the Education unit tests will catch changed numbers in the cases they cover.
  • Landing page. content/apps/landing-education.ts features protein-structure, mutation and dose-response and lists every category. Changing a featured tool's poster or manifest fields changes /landing.json; a change to the exported shape requires bumping LANDING_EXPORT_VERSION (see Deployment and environment).

Tests

Browser tests in e2e/education/ all run with strictOffline: true:

File Covers
workspace.spec.ts Subject groups in the sidebar open the current subject; each tool opens directly as its own workspace with no CSP violations; ATP synthesis against substrates and a leak; gene expression with inducer and repressor; reduced motion keeps manual steps
sequence.spec.ts Mutation Lab's four consequences; translation play, pause, step and reset; PCR bounded duplexes; CRISPR recognition, cleavage and repair
nucleic.spec.ts Replication direction and nicks; splicing codons for the three choices; RNA pairing, rescue and representation; reduced motion and phone use (60 s timeout)
structural.spec.ts Protein structure selections, features and displacement; binding affinity, occupancy and competition; subject groups with short rows
quantitative.spec.ts Dose–response midpoint, range and reproducible readings; serial transfers; buffer reserve and exhaustion; charts follow the suite theme
applied.spec.ts Molecule donor and acceptor changes; feed and aeration; growth timeline manual and usable under reduced motion
structure.spec.ts Replication loads PDB 7PFO in 3D only on request (no Mol* or scene request before), zoom, reset, touch Rotate, and returning to 3D without a second download; the other nine structures each load on the desktop project; /cell/app still loads Cell's own scene file
media.spec.ts Each of the three guides plays on request and stops when returning to the still (skipped on desktop-dark)

applied.spec.ts and nucleic.spec.ts also fail on hydration-mismatch console errors. Test infrastructure is in Build, test and CI.

Known gaps, defects and divergences

Open scientific findings

From the 10 October 2026 scientific audit, not resolved in the code at the time of writing:

  • M11: ATP Synthase and DNA Replication teach nothing a learner meets in a Drybench app; the audit recommends re-pointing ATP Synthase to Bioreactor's oxygen demand or cutting it, and names Replication as the first cut candidate. docs/NEXT.md records that the CEO has set M11 aside for now.
  • The gene-switches source line (content/education.ts, "Lac repressor study", 1LBG) says the structures include inducer; the audit states that is 1LBH, and that allolactose is never named.
  • The synthetic intron's branch-point A is 2 nt from the 3′ AG with no polypyrimidine tract between them (RETAINED_INTRON in experiments/nucleic/models.ts).
  • The acetate pKa 4.756 is the zero-ionic-strength value and ionic strength is never named; the source detail cites "Table 8" where the audit reads "section 8" (content/education-quantitative.ts).
  • In CRISPR's mismatch case the spacer text from position 16 onwards is coloured as mismatched (index >= 16 && !scenario.paired, src/apps/education/experiments/sequence/crispr-lab.tsx:57), which includes position 19, where the spacer and target match.
  • Manifest legends say "Ribosomal and small nuclear RNA" on the ribosome-only mutation scene and the spliceosome-only splicing scene (the label is Cell's category name).
  • The audit also says Dose–Response "promises fitted estimates it never produces"; the current objectives say "Distinguish a prescribed curve from sampled observations and fitted estimates" and the refresher says no regression is calculated.
  • The trapped-state caveats (cycloheximide, ONPF, H840A, ddCTP) are in the closed "Structure details" disclosure, which the audit flags.

Design documents against the code

docs/design/education-expansion-research.md proposed more than was built. Not present: the curated haemoglobin case (PDB 2HBS) in Mutation Lab; a predicted product-length diagram, synthetic gel and no-template control in PCR Lab; the SMN2 case in Splicing; mRNA and protein traces and an ensemble view in Gene Switches; the riboswitch (1Y26) as a structural example in RNA Shape (it is a source link only); and a prediction task before each reveal. docs/design/education-workspaces.md matches the built tools; its validation figures (73 model and viewer tests, 87 browser checks on 7 October 2026) are historical and were not re-run for this page.

Divergences from the repository rules

  • Copy in components. The workspace and experiment components contain their user-facing strings inline (for example "Loading the experiment…", "Check answer", "That’s right.", "Structural reference" and "Play molecular view" in src/apps/education/workspace.tsx, and every label and explanation in the lab files), against the rule that copy lives in content files. Only educationUi.interactiveDisclosure is in content/.
  • App names typed again. connection.label strings name apps by hand ("Open Sequence", "Open Protein", "Open Cell", "Open Plasmid", "Open Assay", "Open Molecule", "Open Bioreactor", and the working titles "Open Assay Analyser" and "Open Lab Calculators" at content/education-quantitative.ts:15 and :36) instead of reading APP_SHORT_NAMES from content/app-names.ts.
  • Sources without licence facts. The Education sources carry no licence id or read date, and the tools are outside the registry checks that enforce these for apps. This is a pre-live compliance item (D-0014).
  • Hosts declared through Cell. The Education pages contact no external host, but their policy comes from Cell's registry entry; a change to Cell's dataPolicy changes all fourteen.

Pipeline fragility

  • molecular.py hard-codes retrieved: "2026-10-07" (pipeline/education/molecular.py:269), so a rebuild keeps an out-of-date retrieval date.
  • molecular.py falls back to a macOS path, /private/tmp/drybench-education-7pfo.bcif (pipeline/education/molecular.py:208), when the 7PFO original is not in the cache.
  • Every run of molecular.py (including --stills alone) rewrites manifest.json through publish_manifest(args.publish_videos) (pipeline/education/molecular.py:399); without --publish-videos the six video links are removed. The README's sequence ends with a --prepare-only --publish-videos run for this reason.
  • prepare.py:72 and build_views.py:121 take the last row of pdbx_audit_revision_history as the current revision, where Cell's latest_revision takes the maximum major and minor number; the two agree only if the rows are in order.
  • The 6Y0G and 6QDV viewer files are byte-for-byte copies of Cell's (about 427 KB served twice). If Cell's files are rebuilt, build_views.py must be rerun or the Education copies keep the old revision.
  • The cached originals in structure-sources/coordinates/ are git-ignored. A rebuild re-downloads the current revisions; prepare.py keeps the old retrievedUtc only when the checksum is unchanged.
  • The pipeline README notes that Blender initialisation on the original macOS host needed unsandboxed execution; the pipeline has not been recorded as run on Linux.
  • workspace.tsx:122 passes poster={media.poster} to the viewer without the ?v= cache key that the visible <img> uses, so the overlay still under a loading 3D scene can be served from an older cache entry after a poster rebuild that keeps the file name.

How to add content safely

Change text in an existing tool

Edit the tool's entry in content/education.ts (or the structural, quantitative or applied file). Keep the checkpoint's answer index pointing at the right choice; nothing tests it. Keep claims inside what the experiment computes and what the cited source supports. For a molecular tool, purpose, limitations, legend and references live in pipeline/education/catalog.json; edit it there and republish with python pipeline/education/molecular.py --prepare-only --publish-videos (which also regenerates scenes.json), or edit catalog.json and manifest.json identically by hand. If a featured tool's manifest fields change, check /landing.json.

Change a model

Change the pure module under src/apps/education/experiments/<area>/ and its test together. Where a model reuses a suite library (translate, fourPL, solveC1V1, the buffer table, RDKit helpers), prefer changing the call over copying the library. For Molecule Properties, rerun node src/apps/education/experiments/applied/build-molecules.mjs after an RDKit upgrade: the test fails if molecules.json was made with a different RDKit version.

Add an interactive tool

  1. Add { id, title, label } to EDUCATION_NAV and the id to one EDUCATION_CATEGORIES group. Check that the id cannot collide with an app segment in content/apps/routes.ts or a reserved path.
  2. Add an EducationTool with presentation: "interactive", at least one source, and a connection href built with appHref.
  3. Create src/app/(workspace)/(education-tools)/<id>/app/page.tsx in the existing two-line form.
  4. Add the experiment component and its pure model with a Node test, and add it to EXPERIMENTS in workspace.tsx.
  5. Add a mark for the id in src/components/suite/education-mark.tsx.
  6. Update the counts in content/education.test.ts (14 tools, 4 interactive) and add browser checks under e2e/education/.
  7. Run npm test, npm run lint, npx tsc --noEmit, npm run build and npm run test:e2e.

Add a molecular tool

In addition to the steps above (without presentation: "interactive"):

  1. Add the entry to SOURCES in structure-sources/prepare.py and TOOLS in build_views.py, with any trimming rule; add a selection block to render-selections.json (label ids only, groups with entities, styles and colours, limitations).
  2. Add the id to KEYS in molecular.py and to category_maps, with labels in labels_for and any camera rule in auto_camera.
  3. Add the asset to catalog.json with id, poster, alt, purpose, limitations, legend (colour words from molecularColours), references, sceneId, pdbId, structure.
  4. Run prepare.py, build_views.py, molecular.py --stills --scenes <id> and molecular.py --prepare-only --publish-videos, then verify.py. Update the counts that verify.py (10 assets, 3 videos) and content/education.test.ts (10 manifest assets) assert, and bump version in catalog.json with new file names if a poster changes, so cached copies are not shown.
  5. Inspect the poster and the 3D view side by side; verify.py checks files, not science or framing.
  6. Add the structure to the parametrised list in e2e/education/structure.spec.ts.

Do not reuse a Cell colour category for a different molecule type without stating the meaning in the asset's legend and the group's label, because the 3D tooltip and the legend are the only places the meaning is shown.