An open, curriculum-aware schema for educational content.
EduVis describes the educational meaning of learning experiences — what elements are, what they do, how they relate, where they belong, and how the lesson flows. Renderers translate that meaning into SVG, Canvas, React, Flutter, PDF, or animated video.
Inspired by the philosophy behind Markdown, Mermaid, and Model Context Protocol (MCP):
Separate meaning from rendering.
Requirements: Python 3.10+
pip install eduvisgit clone https://github.com/seehiong/eduvis
cd eduvis
uv syncThen prefix commands with uv run (or use the globally installed eduvis if installed via PyPI/pip):
# Validate showcase lessons
uv run eduvis validate docs/showcase/lesson-negative-numbers.yaml
uv run eduvis validate docs/showcase/lesson-geometry-triangles.yaml
uv run eduvis validate docs/showcase/demo-adaptive-remediation.yaml
# Render showcase lessons to SVGs
uv run eduvis render docs/showcase/lesson-negative-numbers.yaml -o output/negatives/
uv run eduvis render docs/showcase/lesson-geometry-triangles.yaml -o output/geometry/
uv run eduvis render docs/showcase/demo-adaptive-remediation.yaml -o output/remediation/
# Utility commands
uv run eduvis docs --subjects math
uv run eduvis schema -o schemas/git clone https://github.com/seehiong/eduvis
cd eduvis
pip install -e .uv sync --extra dev
uv run pytest tests/ -vTo see every registered element type rendered to SVG in one pass:
uv run eduvis render docs/examples/renderer_test.yaml -o output/renderer_test/This produces one SVG per element type (test_number_line.svg, test_text_list.svg, test_math_grid.svg, test_solid_cube.svg, … test_solid_cylinder.svg) — useful for checking renderer output after code changes.
EduVis is not an SVG schema.
EduVis is a machine-readable instructional model.
It captures the structure of good tutoring — the same structure behind effective human instructors: no skipped steps, visual intuition before abstraction, confidence-building before challenge, and retrieval to lock it in long-term.
EduVis is to educational experiences what Markdown is to documents and Mermaid is to diagrams.
A specification can be rendered as SVG, PDF, slides, interactive lessons, or animated videos while preserving pedagogical intent.
Most diagram libraries describe visuals. EduVis describes learning experiences.
A number line in a textbook, a number line used to discover a rule, and a number line shown during a recall exercise are pedagogically different objects. They happen to look the same. Today's tools treat them identically.
# What every library gives you
type: number_line
range: [-10, 10]
highlight: [-3, 5]EduVis preserves the meaning that gets lost the moment most tools export to SVG:
type: number_line
placement:
lesson_phase: explore
memory_role: anchor
difficulty: starter
actions:
- compare: [-3, 5]
range: [-10, 10]But more importantly, EduVis describes where this element sits inside a proven teaching pattern — something no diagram library models at all.
This is not a theoretical schema. The placement model, element types, and LLM prompt vocabulary have been validated in real educational pipelines and are designed for production use.
- Interactive Showcase: View complete, production-grade lessons rendered to SVG.
- Live Schema Editor: Write and preview your own EduVis YAML specifications in real-time. It runs the full Python rendering library entirely client-side in the browser using WebAssembly (Pyodide).
EduVis is deliberately split into two companion specifications.
EduVis-Core
↓
EduVis-Presentation
The educational meaning layer. Renderer-agnostic, stable, and the rare part.
Covers five concerns:
| Concern | What it answers |
|---|---|
| Elements | What content type is this? |
| Actions | What does this element ask the student to do? |
| Relationships | How does this element relate to others in the lesson? |
| Placement | Where does it live in the lesson and in memory? |
| Progression | What is the instructional flow of the whole lesson? |
The animation and timing layer. Renderer-specific, layered on top of Core.
Covers: zoom, pan, pause, reveal sequencing, highlight animation, narration timing.
Why the split matters: mixing these too early produces a video animation engine, not an educational standard. The educational semantics are the rare and valuable part. Animation is comparatively easy to layer on later.
A Core spec can be rendered to a static PDF today and to a YouTube-style animated lesson tomorrow — without changing a single field.
The content types. Educational primitives, not drawing primitives.
type: number_line
range: [-10, 10]
highlight:
- value: -3
label: "-3°C"
color: blue
- value: 3
label: "3°C"
color: red
direction_labels:
left: Colder
right: Warmernumber_line, fact_boxes, multiple_choice, hint_list — these are pedagogical roles, not visual shapes. See Element Reference for the full vocabulary.
What the element asks the student to do — the educational intent of the interaction.
actions:
conceptual:
- compare: [-3, 5] # notice a difference between two values
- predict: unknown # student fills in a missing value
- identify: misconception # student spots the error before it is revealed
- retrieve: rule # student recalls without looking back
- apply: signed-number-ordering # student applies a rule to a new case
procedural:
- substitute: { from: x, to: 3 } # step-by-step transformation
- simplify # reduce an expression
- calculate # perform arithmetic
- round: { decimal_places: 2 }Actions are split into two categories:
Conceptual — what the student does cognitively:
| Action | What it means |
|---|---|
compare |
Draw attention to two elements in relation |
predict |
Student must supply a value before it is revealed |
identify |
Student spots the error or pattern before explanation |
retrieve |
Student recalls from memory without re-reading |
apply |
Student applies a rule to a new case |
Procedural — step-by-step mathematical transformations (the no-skipped-steps principle):
| Action | What it means |
|---|---|
substitute |
Replace a variable or expression — one explicit step |
simplify |
Reduce an expression — one explicit step |
calculate |
Perform arithmetic — one explicit step |
round |
Round to a specified precision — one explicit step |
Actions are not animation instructions. compare does not mean "animate an arrow between two values." It means "this element exists to make a comparison salient." The presentation layer decides how.
Procedural actions enforce no_skipped_steps: every transformation is named, so an AI generator cannot collapse two steps into one and a renderer can show working line by line.
How elements relate to other elements within a lesson. Enables lesson-level coherence checking and AI lesson assembly.
relationships:
anchors:
- fraction_model # this element is the concrete anchor for the concept
contradicts:
- misconception_example # this element corrects the previous one
precedes:
- practice_question # this element scaffolds the next element
reinforces:
- hook_scenario # this element brings back the opening memory| Relationship | What it means |
|---|---|
anchors |
This element establishes the concrete memory anchor for a concept |
contradicts |
This element corrects or challenges a previous element |
precedes |
This element scaffolds the element that follows |
reinforces |
This element recalls an earlier anchor to strengthen it |
parallels |
Two elements show the same concept at different abstraction levels |
remediation_for |
This element is shown when a student fails a linked element — scaffolds a retry |
Adaptive tutoring pattern — the remediation_for relationship encodes the CHECK → HINT branching loop used in intelligent tutoring systems without requiring a separate adaptive-paths pillar:
- id: check_prime_identification
type: multiple_choice
placement:
lesson_phase: independent_practice
memory_role: practice
difficulty: starter
question: Which of these is a prime number?
options: {A: "1", B: "2", C: "4", D: "6"}
answer: "B"
- id: hint_prime_identification
type: hint_list
placement:
lesson_phase: guided_practice
memory_role: example
purpose: worked_example
relationships:
remediation_for:
- check_prime_identification
items:
- "List the factors of each option"
- "A prime has exactly two factors: 1 and itself"
final: "Choose the number with exactly two factors"The runtime reads remediation_for and branches: show check_prime_identification first; if the student answers incorrectly, show hint_prime_identification and retry. The progression block still declares guided_practice before independent_practice — preserving pedagogical intent regardless of document order.
Where the element lives in the lesson and in long-term memory. Three independent layers.
placement:
# Layer 1 — Layout: where on the screen
layout_zone: center # center | left | right | full | bottom
visual_weight: primary # primary | supporting
# Layer 2 — Pedagogical: where in the lesson
lesson_phase: explain # hook | explore | explain | guided_practice | independent_practice | challenge | reflect | recall
purpose: conceptual_model # conceptual_model | worked_example | comparison | procedure | summary
difficulty: routine # starter | routine | challenge (meaningful in practice phases)
# Layer 3 — Memory: what role in retention
memory_role: anchor # anchor | example | practice | misconception_fix | retrieval | reviewLesson phases:
| Phase | What it means |
|---|---|
hook |
Concrete scenario before the concept is named |
explore |
Student observes a pattern before the rule is stated |
explain |
Rule or concept is revealed — conceptual_model purpose before procedure |
guided_practice |
Instructor walks through a worked example with the student following each step |
independent_practice |
Student applies the concept without guidance; difficulty set by difficulty field |
challenge |
Stretch problem that extends beyond routine application |
reflect |
Student articulates what they learned |
recall |
Student retrieves without re-reading — builds long-term memory |
Difficulty levels (used in independent_practice and challenge phases):
| Level | What it means |
|---|---|
starter |
Intentionally easy — builds confidence before the concept feels hard |
routine |
Typical exam-style problems — the core of independent practice |
challenge |
Harder realistic problems or stretch questions |
Memory roles:
| Role | What it means |
|---|---|
anchor |
The one element the student should remember weeks later |
example |
Demonstrates the concept in a specific case |
practice |
Used during in-lesson application |
misconception_fix |
Corrects a specific common error |
retrieval |
Shown during a recall exercise |
review |
Appears in a future lesson as a spaced repetition cue |
The instructional flow of the whole lesson. This is the pillar that makes EduVis more than a diagram library.
Progression operates at the lesson level. Placement operates at the element level. Together they encode the teaching pattern — not just the individual elements, but the sequence that makes learning stick.
progression:
pattern: confidence_ladder # the named teaching pattern
pedagogy:
confidence_first: true # begin with starter problems before routine ones
explain_why: true # conceptual_model purpose before procedure
no_skipped_steps: true # every transformation is an explicit action
phases:
- phase: hook
- phase: explore
- phase: explain
purpose: conceptual_model
- phase: explain
purpose: procedure
- phase: guided_practice
count: 1
- phase: independent_practice
difficulty: starter
count: 3
- phase: independent_practice
difficulty: routine
count: 5
- phase: challenge
count: 1
- phase: recall
count: 2Named patterns:
| Pattern | What it means |
|---|---|
confidence_ladder |
Hook → Explore → Explain → Guided → Starter Practice → Routine Practice → Challenge → Recall. No steps skipped, confidence built before complexity introduced. |
direct_instruction |
Hook → Explain → Guided → Independent Practice → Recall. Shorter sequence for procedural topics where exploration is less useful. |
flipped_recall |
Recall → Hook → Explore → Explain → Practice. Opens with retrieval to activate prior knowledge before new content. |
Pedagogy flags:
| Flag | What it means |
|---|---|
confidence_first |
starter difficulty problems appear before routine ones |
explain_why |
A conceptual_model element precedes the procedure element |
no_skipped_steps |
Every mathematical transformation is an explicit action |
A single element from the Negative Numbers lesson — explore phase, number line, showing two temperatures for comparison:
- id: explore_number_line
type: number_line
concepts:
- negative_numbers
placement:
lesson_phase: explore
memory_role: anchor
difficulty: starter
actions:
conceptual:
- compare: [-12, 32]
relationships:
anchors:
- hook_temperature
range: [-15, 35]
highlight:
- value: -12
label: "Antarctica"
color: blue
- value: 32
label: "Singapore"
color: redThis element sits inside a confidence_ladder lesson that progresses through hook → explore → explain → guided practice → starter problems → routine problems → challenge → recall.
See docs/showcase/lesson-negative-numbers.yaml for the full lesson spec.
The insight from studying effective human tutors: the diagrams are about 20% of the value. The other 80% is the sequence.
Without EduVis, an AI lesson generator asks:
Draw three diagrams about negative numbers.
With EduVis, it asks:
I need one
anchorelement in theexplorephase, aconceptual_modelbefore theprocedurein theexplainphase, oneguided_practiceworked example, threestarterpractice problems to build confidence, fiveroutineproblems, onechallenge, and tworetrievalitems — followingconfidence_ladderwithexplain_whyandno_skipped_steps.
The generator is no longer assembling graphics. It is assembling a learning experience — one that follows the same structure that makes great human tutors effective.
The instructional patterns that live inside the heads of great tutors become explicit, machine-readable, and consistently reproducible. That is the gap no SVG library, diagram tool, or LLM prompt currently fills.
EduVis exposes its full five-pillar vocabulary as a runtime-generated system prompt block — the same pattern MCP uses to announce tool schemas to a model.
Inject it before asking an LLM to write lesson specs:
from eduvis.core import format_prompt_docs
system_prompt = f"""
You are an instructional designer writing EduVis lesson specs.
Follow the schema exactly — only use elements, phases, and actions listed below.
{format_prompt_docs(["math"])}
"""Or generate it from the CLI:
python -m eduvis docs --subjects math
python -m eduvis docs --subjects math --output vocab.txtThe vocabulary covers all five pillars in one block: lesson skeleton, progression patterns, placement model, actions vocabulary, relationship types, and element field schemas — everything an LLM needs to produce a valid spec without guessing at field names.
Sample output — the block below is generated from python -m eduvis docs --subjects math at the current release. It is illustrative, not exhaustive; the actual output updates automatically as elements are added.
View sample LLM vocabulary (v0.1, math)
Full reference: docs/llm_system_prompt.md
## EduVis Lesson Structure
Every lesson YAML has three top-level keys: lesson, progression, content.
lesson:
syllabus: string # curriculum code e.g. "SEC-math-2027"
topic: string # topic code e.g. "N1.6"
title: string # human-readable lesson title
concepts: # optional list of target concepts
- string
progression:
pattern: confidence_ladder | direct_instruction | flipped_recall
pedagogy:
confidence_first: true | false
explain_why: true | false
no_skipped_steps: true | false
phases:
- phase: <lesson_phase>
purpose: <purpose> # optional, use in explain phase
difficulty: <difficulty> # optional, use in practice phases
count: <integer> # optional, number of elements in this phase
content:
- id: string # unique identifier within the lesson
type: <element_type> # see Element Types below
concepts: # optional list of concepts taught by this element
- string
placement:
lesson_phase: hook | explore | explain | guided_practice |
independent_practice | challenge | reflect | recall
memory_role: anchor | example | practice | misconception_fix |
retrieval | review
difficulty: starter | routine | challenge # optional
purpose: conceptual_model | procedure | worked_example |
comparison | summary # optional
actions: # optional
conceptual:
- compare: [-3, 5]
- predict: unknown
procedural:
- substitute: {from: x, to: 3}
- simplify
relationships: # optional
anchors: [hook_temperature]
precedes: [practice_starter_1]
remediation_for: [check_element_id]
<element-specific fields...>
## Element types (math, v0.1)
number_line range, highlight, direction_labels, caption
fraction_model shape: circle|bar|grid, total_parts, shaded_parts
bar_model bars: [{label, value, color}], difference
coordinate_plane x_range, y_range, plots: [{type, equation, color}]
geometry_shape vertices, labels, side_labels, angles
solid_shape shape: cube|cone|cylinder|pyramid, dimensions, color
factor_array number: N
math_grid rows: [[cells]], headers
text_list items: [strings]
fact_boxes items: [{text, border_color}]
example_panel items: [{heading, body}]
callout_box title, lines, border_color
summary_list items: [strings]
multiple_choice question, options: {A, B, C, D}
hint_list items: [strings], final: string
The solid_shape element renders 3D solids using isometric projection—perfect for teaching volume, surface area, and spatial reasoning.
type: solid_shape
shape: cylinder # cube, rectangular_prism, pyramid, cone, cylinder, etc.
dimensions: [3, 5] # [width, height] for cone/cylinder; [w, h, d] for prism
color: blue
label: "Volume = πr²h" # optional label below shape
show_dimensions: true # optional: overlay radius/height measurements on shapeSupported shapes:
cube— regular cube (use single dimension:[side])rectangular_prism— box with custom width, height, depth ([w, h, d])triangular_prism— prism with triangular cross-sectionpyramid— square pyramid with apexcone— circular cone with apex ([diameter, height])cylinder— circular cylinder ([diameter, height])
Features:
- Isometric projection — automatic 3D perspective from 2D coordinates
- Auto-scaling — all shapes fit the content zone, dimensions stay proportional
- Dimension labels — use
show_dimensions: trueto overlay radius/height measurements (yellow text) - Custom labels —
labelfield renders below the shape for captions or formulas
Cylinder improvements:
- Darker bottom, lighter top to emphasize 3D depth
- All 16 vertical edges visible
- Bold top edge outline
See docs/examples/renderer_test.yaml for live examples: test_solid_cube.svg, test_solid_cone.svg, test_solid_cylinder.svg, etc.
You can easily integrate eduvis validation and prompt generation into your custom Python scripts and LangChain pipelines.
Validate lesson specs dynamically:
import yaml
from eduvis.core import validate_lesson
# Load your generated lesson YAML
with open("lesson.yaml", "r") as f:
lesson_data = yaml.safe_load(f)
# Run the five-pillar validator
warnings = validate_lesson(lesson_data)
if not warnings:
print("Lesson is valid!")
else:
print(f"Validation warnings: {warnings}")Retrieve the vocabulary string to inject directly into your LangChain prompt templates:
from langchain_core.prompts import ChatPromptTemplate
from eduvis.core import format_prompt_docs
# Generate curriculum-specific vocab docs (e.g., for Mathematics)
eduvis_vocab = format_prompt_docs(["math"])
# Inject into LangChain Prompt Templates
prompt = ChatPromptTemplate.from_messages([
("system", "You are an instructional designer. Use the following EduVis schema rules:\n\n{vocab}"),
("human", "Generate a lesson spec for: {topic}")
])
chain = prompt | llm
# result = chain.invoke({"vocab": eduvis_vocab, "topic": "adding fractions"})Current focus: Mathematics.
Science and Humanities element types and renderers are planned for a future release.
docs/examples/renderer_test.yaml contains one working slide per element type. Run it with
eduvis renderto get a visual catalog of every renderer.
| Element | Synopsis |
|---|---|
text_list |
items: [strings] |
fact_boxes |
items: [{text, color}] |
example_panel |
items: [{heading, body}] |
callout_box |
title, lines, color |
summary_list |
items: [strings] — use on closing elements |
multiple_choice |
question, options: {A, B, C, D} |
hint_list |
items: [strings], final: string |
number_line |
range, highlight, direction_labels, caption |
| Element | Synopsis |
|---|---|
fraction_model |
shape: circle|bar|grid, total_parts, shaded_parts |
bar_model |
bars: [{label, value, color}], difference |
coordinate_plane |
x_range, y_range, plots: [{type, equation, color}] |
geometry_shape |
vertices, labels, side_labels, angles |
factor_array |
number: N — dot grid for factors and primes |
math_grid |
rows: [[cells]], headers — column arithmetic |
- Formal JSON Schema for all element types
- Placement model: all three layers, including
difficulty - Actions vocabulary: initial set including step-by-step transformation actions
- Progression model: named patterns and pedagogy flags
- Built-in SVG reference renderer
- Secondary Mathematics examples
- Relationships between elements within a lesson
- Curriculum tagging (
syllabus,topic) - Lesson-level coherence validation against the declared progression pattern
- Reveal sequencing
- Narration timing hooks
- Highlight and zoom annotations
- Designed to layer cleanly over Core — no Core schema changes required
eduvis/ (repository root)
├── pyproject.toml ← package metadata and dependencies
├── uv.lock ← pinned dependency versions
├── LICENSE ← Apache 2.0 License
├── README.md ← this documentation file
├── .gitignore ← untracked files to ignore
│
├── eduvis/ ← Python package source code
│ ├── __init__.py ← package entrypoint & exported APIs
│ ├── __main__.py ← entrypoint for running directly as a script
│ ├── cli.py ← Click CLI commands implementation
│ │
│ ├── core/ ← EduVis-Core: schema, validation, prompt vocabulary
│ │ ├── registry.py ← ElementRegistry (specifications list + prompt docs)
│ │ ├── validator.py ← five-pillar lesson validator
│ │ ├── prompt.py ← format_prompt_docs() for LLM prompts
│ │ ├── elements/
│ │ │ ├── generic.py ← generic element field definitions
│ │ │ └── math.py ← mathematics element field definitions
│ │ └── schemas/
│ │ ├── placement.py ← schema definitions for placement (phases, roles)
│ │ ├── actions.py ← schema definitions for actions
│ │ ├── relationships.py ← schema definitions for relationships
│ │ └── progression.py ← schema definitions for progression patterns
│ │
│ ├── renderers/
│ │ └── svg/ ← Python reference renderer (SVG output)
│ │ ├── spec_renderer.py ← SVGSpecRenderer — YAML spec to SVG
│ │ ├── primitives.py ← canvas constants and drawing helpers
│ │ ├── renderers_base.py ← generic element renderers
│ │ └── renderers_math/ ← mathematics element renderers
│ │
│ └── schemas/ ← pre-generated JSON Schema files packaged with the library
│ ├── placement.schema.json
│ ├── actions.schema.json
│ ├── relationships.schema.json
│ ├── progression.schema.json
│ └── lesson.schema.json
│
├── docs/ ← Documentation and showcase files
│ ├── llm_system_prompt.md ← generated vocabulary reference for LLMs
│ ├── examples/
│ │ ├── mixed_card_demo.yaml ← mixed card layout demo (decimal subtraction)
│ │ └── renderer_test.yaml ← one slide per element type (visual catalog)
│ └── showcase/
│ ├── lesson-negative-numbers.yaml ← canonical negative numbers spec
│ ├── lesson-geometry-triangles.yaml ← geometry shape triangles lesson spec
│ └── demo-adaptive-remediation.yaml ← adaptive remediation flow spec
│
├── schemas/ ← pre-generated JSON Schema files at the repository root
│ ├── placement.schema.json
│ ├── actions.schema.json
│ ├── relationships.schema.json
│ ├── progression.schema.json
│ └── lesson.schema.json
│
└── tests/ ← Test suite
├── test_validate.py ← validator smoke tests
└── test_schema_export.py ← JSON Schema export smoke tests
Learning Intent
↓
EduVis-Core (educational meaning — stable, renderer-agnostic)
Elements · Actions · Relationships · Placement · Progression
↓
EduVis-Presentation (timing, animation — renderer-specific, layered on top)
↓
Any target: SVG · React · Flutter · PDF · YouTube · Interactive platform
Just as Markdown became the standard for text, EduVis aims to become the standard for educational content — where progression, placement, and actions are as important as the element itself.
Early design. Reference implementation live in Nova Tutor (Singapore Secondary Mathematics).
Contributions and feedback welcome.
This project is licensed under the Apache License 2.0. See LICENSE for details.