An interactive reader for Spinoza's Ethics built in React and TypeScript.
The project combines:
- structured XML editions of the text
- a Latin-aligned parallel text layer keyed to the same passage ids
- an explicit N3 graph of logical relationships
- EYE-based inferred relationships
- a corpus generation step that keeps XML and graph data aligned
- a reader UI designed for navigation, cross-reference tracing, and close reading
Live site: rdaum.github.io/ethica
The reader currently supports Parts I-V of the Ethics in the Elwes translation, with:
- ordered reading flow rather than a flat “demo app” section dump
- support for nested material such as physical axioms, lemmas, postulates, notes, explanations, and appendix sections
- passage selection with logical analysis in a side panel
- cross-navigation between cited and related passages
- URL deep-linking to individual passages
- part-local search
- inferred dependency and transitive relationship display
The app has three main layers:
-
Text layer XML files in
public/ethica_1.xmlthroughpublic/ethica_5.xmlare parsed into a typed in-memory reader model. Latin alignment files inpublic/ethica_la_1.jsonthroughpublic/ethica_la_5.jsonattach bilingual text to the same canonical ids. -
Graph layer
public/ethica-logic.n3contains explicit graph data.public/ethica-logic-eye.n3contains both explicit triples and rule-based inference input. -
Reader layer React components render the ordered text, section navigation, search, and analysis panel.
src/
├── App.tsx # Application shell, loading, navigation, selection state
├── types.ts # Shared reader and analysis types
├── lib/
│ ├── ethica.ts # XML parsing, labels, section summaries, graph helpers
│ ├── ethica.test.ts # Parser-focused tests
│ ├── readerGraph.ts # Supplemental graph generation from parsed text
│ └── readerGraph.test.ts # Supplemental graph tests
└── components/
├── BookView.tsx # Main reading surface
└── ReasoningPanel.tsx # Logical analysis panel
scripts/
├── generate-corpus.mjs # Regenerates Parts I-V XML and the explicit graph
└── generate-latin.mjs # Aligns Latin Library text to the canonical corpus ids
public/
├── ethica_1.xml # Part I markup
├── ethica_2.xml # Part II markup
├── ethica_3.xml # Part III markup
├── ethica_4.xml # Part IV markup
├── ethica_5.xml # Part V markup
├── ethica_la_1.json # Part I Latin alignment
├── ethica_la_2.json # Part II Latin alignment
├── ethica_la_3.json # Part III Latin alignment
├── ethica_la_4.json # Part IV Latin alignment
├── ethica_la_5.json # Part V Latin alignment
├── ethica-logic.n3 # Explicit graph
├── ethica-logic-eye.n3 # Explicit triples + inference rules
└── spinoza-signet.png # Reader branding asset
npm install
npm startUseful commands:
npm run generate:corpus
npx tsc --noEmit
CI=true npm test -- --watchAll=false
npm run buildThe reader now treats the generated explicit graph as the primary source of structural and citation relationships.
The canonical passage structure is now Latin-governed. In practice that means:
- the English XML is the reader's structural source
- the Latin alignment script validates that this structure can be walked in the order signaled by the original Latin
- obvious English-only editorial additions are preserved, but marked as editorial rather than treated as authorial structure
- Part IV appendix chapters follow the Latin
CAPUTstructure instead of anonymous synthetic argument buckets
At runtime it still builds a supplemental graph from the parsed XML in order to provide backfill only when the canonical graph lacks a relationship. That backfill covers:
- structural relationships such as
partOf,provedBy,hasCorollary, andhasNote - citation coverage extracted from prose references when an explicit triple is absent
The canonical graph files are regenerated from the corpus, so the XML and N3 layers no longer drift independently.
What is in relatively good shape:
- Parts I-V render in reading order
- Parts I-V are generated into XML from
ethica.txt - Parts I-V also have Latin parallel text aligned to the same passage ids
- the explicit graph is regenerated from the corpus instead of being maintained only as ad hoc hand-edited triples
- build, tests, and TypeScript checks pass
- the main bundle is much smaller than before due to lazy loading of inference work
- the reader UI is no longer structured like a prototype shell
What is still incomplete:
- the graph is much more coherent, but it is still a reader-oriented reference graph rather than a full scholarly critical apparatus
- inference remains useful but is not yet a polished scholarly model
- there is not yet a dedicated editorial normalization pipeline for harmonizing every source-text edge case across all five parts
The highest-value next steps are:
- improve passage-level metadata and editorial annotations
- add better tests around navigation and analysis behavior
- consider replacing the aging CRA/CRACO stack with a more current frontend toolchain
Uses the English translation by R.H.M. Elwes (1883) from Project Gutenberg #3800. The text is public domain.