← Back to documentation

Visual Primitives Specification

Version: 0.1 (Draft)
Date: March 2026
Status: Proposal
Depends on: Stroke Format Spec
Used by: Bridge, Edge, Tutor


1. Overview

Visual Primitives are the device-agnostic "drawing commands" that Bridge sends to Edge. They define what to render without specifying how each platform renders it.

Think of this as the "assembly language" between Tutor's semantic intent and Edge's native rendering.

1.1 Design Goals

Goal Implication
Edge-agnostic Same primitives work on BOOX, reMarkable, iPad, web
Capability-adaptive Graceful degradation when Edge lacks features (e.g., no color)
Minimal Small vocabulary — easy to implement on new Edge platforms
Composable Complex visuals built from simple primitives
Semantic hints Include why something is drawn, not just what

1.2 Non-Goals


2. Coordinate System

All primitives use normalized coordinates matching the Stroke Format:

Sizes and dimensions also normalized (e.g., width: 0.1 = 10% of content width).


3. Primitive Categories

┌─────────────────────────────────────────────────────────────┐
│                    VISUAL PRIMITIVES                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  CONTENT           ANNOTATION         FEEDBACK              │
│  ─────────         ──────────         ────────              │
│  Text              Highlight          Mark (✓/✗)            │
│  MathBlock         Underline          Pulse                 │
│  Image             Circle             Pointer               │
│  Line              Arrow                                    │
│  Box               Bracket            CONTROL               │
│  Divider           Strikethrough      ───────               │
│                                       Clear                 │
│  INPUT             HINT               ClearLayer            │
│  ─────             ────               Transition            │
│  InputZone         HintBubble                               │
│  InputGuide        HintPointer                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4. Primitive Definitions

4.1 Content Primitives

Content primitives render static exercise content.

TEXT

Renders text at a position.

{
  "type": "TEXT",
  "id": "txt_001",
  "content": "Solve for x:",
  "position": {"x": 0.05, "y": 0.08},
  "style": {
    "size": "medium",
    "weight": "normal",
    "align": "left"
  }
}
Field Type Required Values
content string UTF-8 text
position {x, y} Normalized coordinates
style.size enum small, medium, large, xlarge
style.weight enum normal, bold
style.align enum left, center, right
style.semantic enum body, heading, label, caption

Semantic hint: The semantic field tells Edge the purpose, allowing platform-appropriate rendering (e.g., e-ink might render headings with more contrast).

MATH_BLOCK

Renders mathematical notation.

{
  "type": "MATH_BLOCK",
  "id": "math_001",
  "content": "3x + 5 = 14",
  "format": "asciimath",
  "position": {"x": 0.1, "y": 0.15},
  "size": "large"
}
Field Type Required Values
content string Math notation
format enum asciimath, latex, mathml
position {x, y} Normalized coordinates
size enum small, medium, large

Note: Edge must include a math renderer (e.g., KaTeX, MathJax). If unsupported, fall back to plain text.

IMAGE

Renders an image.

{
  "type": "IMAGE",
  "id": "img_001",
  "src": "asset://exercise/graph_001.png",
  "bounds": {"x": 0.1, "y": 0.3, "w": 0.4, "h": 0.3},
  "alt": "Coordinate plane with point at (3, 4)"
}
Field Type Required Values
src string Asset URI or data URI
bounds {x, y, w, h} Bounding rectangle
alt string Accessibility description
grayscale bool Force grayscale (for e-ink)

LINE

Renders a straight line.

{
  "type": "LINE",
  "id": "line_001",
  "from": {"x": 0.1, "y": 0.5},
  "to": {"x": 0.9, "y": 0.5},
  "style": {
    "weight": "normal",
    "pattern": "solid"
  }
}
Field Type Values
from {x, y} Start point
to {x, y} End point
style.weight enum hairline, normal, thick
style.pattern enum solid, dashed, dotted

BOX

Renders a rectangle (outline or filled).

{
  "type": "BOX",
  "id": "box_001",
  "bounds": {"x": 0.05, "y": 0.1, "w": 0.9, "h": 0.2},
  "style": {
    "fill": "none",
    "stroke": "normal",
    "corner": "rounded"
  }
}
Field Type Values
bounds {x, y, w, h} Bounding rectangle
style.fill enum none, light, medium
style.stroke enum none, hairline, normal, thick
style.corner enum square, rounded

DIVIDER

Renders a horizontal divider line (semantic: section break).

{
  "type": "DIVIDER",
  "id": "div_001",
  "y": 0.45,
  "inset": 0.05
}
Field Type Description
y float Vertical position
inset float Horizontal margin from edges

4.2 Input Primitives

Define where and how students can write.

INPUT_ZONE

Defines an area where student writing is expected.

{
  "type": "INPUT_ZONE",
  "id": "input_answer",
  "bounds": {"x": 0.1, "y": 0.5, "w": 0.8, "h": 0.15},
  "label": "Your answer",
  "semantic": "answer",
  "showBorder": true
}
Field Type Required Description
bounds {x, y, w, h} Writable area
label string Hint text (shown until writing starts)
semantic enum answer, work, scratch, equation
showBorder bool Draw border around zone

Semantic zones: Vision uses semantic to interpret strokes differently:

INPUT_GUIDE

Visual guides for structured input (e.g., grid lines, ruled lines).

{
  "type": "INPUT_GUIDE",
  "id": "guide_001",
  "bounds": {"x": 0.1, "y": 0.5, "w": 0.8, "h": 0.3},
  "guideType": "ruled",
  "spacing": 0.03
}
Field Type Values
guideType enum ruled (horizontal lines), grid, graph, none
spacing float Distance between lines (normalized)

4.3 Annotation Primitives

Tutor-driven overlays on student work. These render on a separate layer above content and strokes.

HIGHLIGHT

Highlights a rectangular region.

{
  "type": "HIGHLIGHT",
  "id": "hl_001",
  "bounds": {"x": 0.3, "y": 0.52, "w": 0.15, "h": 0.04},
  "intent": "warning",
  "opacity": 0.3
}
Field Type Required Values
bounds {x, y, w, h} Region to highlight
intent enum focus, warning, error, success
opacity float 0.0 - 1.0 (default 0.3)

Intent colors (Edge interprets per capability):

Intent Color display E-ink (no color)
focus Blue Light gray fill
warning Yellow/orange Dashed border
error Red Dark border + pattern
success Green Light fill + checkmark

UNDERLINE

Underlines a region (typically under text or math).

{
  "type": "UNDERLINE",
  "id": "ul_001",
  "from": {"x": 0.3, "y": 0.56},
  "to": {"x": 0.45, "y": 0.56},
  "intent": "error",
  "style": "wavy"
}
Field Type Values
from, to {x, y} Line endpoints
intent enum focus, warning, error, success
style enum solid, wavy, double

CIRCLE

Draws a circle around a region (for emphasis).

{
  "type": "CIRCLE",
  "id": "circ_001",
  "center": {"x": 0.4, "y": 0.54},
  "radius": 0.05,
  "intent": "focus"
}

ARROW

Draws an arrow pointing to something.

{
  "type": "ARROW",
  "id": "arr_001",
  "from": {"x": 0.6, "y": 0.4},
  "to": {"x": 0.42, "y": 0.52},
  "intent": "focus"
}

BRACKET

Draws a bracket grouping a region.

{
  "type": "BRACKET",
  "id": "brk_001",
  "from": {"x": 0.25, "y": 0.5},
  "to": {"x": 0.25, "y": 0.6},
  "side": "left",
  "intent": "focus"
}
Field Type Values
from, to {x, y} Bracket endpoints
side enum left, right, top, bottom

STRIKETHROUGH

Strikes through content (for corrections).

{
  "type": "STRIKETHROUGH",
  "id": "strike_001",
  "from": {"x": 0.3, "y": 0.54},
  "to": {"x": 0.5, "y": 0.54},
  "intent": "error"
}

4.4 Feedback Primitives

Quick visual feedback on student work.

MARK

Shows a check or X mark.

{
  "type": "MARK",
  "id": "mark_001",
  "position": {"x": 0.85, "y": 0.53},
  "markType": "correct",
  "size": "medium"
}
Field Type Values
markType enum correct (✓), incorrect (✗), partial (△)
size enum small, medium, large

PULSE

Brief attention-drawing pulse on a region (Edge controls animation).

{
  "type": "PULSE",
  "id": "pulse_001",
  "center": {"x": 0.4, "y": 0.54},
  "radius": 0.08,
  "intent": "focus"
}

On e-ink: may render as a brief invert or thickened border. On LCD: may animate as expanding/fading circle.

POINTER

Shows a pointing indicator (like a finger pointing).

{
  "type": "POINTER",
  "id": "ptr_001",
  "position": {"x": 0.38, "y": 0.52},
  "direction": "right"
}

4.5 Hint Primitives

Hints from the Tutor.

HINT_BUBBLE

Shows a hint in a speech-bubble style.

{
  "type": "HINT_BUBBLE",
  "id": "hint_001",
  "content": "What happens when you move a term to the other side?",
  "anchor": {"x": 0.4, "y": 0.5},
  "position": "above",
  "size": "medium"
}
Field Type Values
content string Hint text
anchor {x, y} Point the bubble points to
position enum above, below, left, right

HINT_POINTER

Arrow from hint bubble to target (if hint and target are separated).

{
  "type": "HINT_POINTER",
  "id": "hintptr_001",
  "from": {"x": 0.5, "y": 0.35},
  "to": {"x": 0.42, "y": 0.52}
}

4.6 Control Primitives

Commands that change state rather than draw.

CLEAR

Clears annotations or content.

{
  "type": "CLEAR",
  "target": "annotations"
}
Target Effect
annotations Clear all annotation layer primitives
hints Clear hint bubbles and pointers
feedback Clear marks, pulses, pointers
all Clear everything (full reset)

CLEAR_ID

Clears a specific primitive by ID.

{
  "type": "CLEAR_ID",
  "id": "hl_001"
}

TRANSITION

Signals Edge to prepare for content change (allows Edge to optimize refresh).

{
  "type": "TRANSITION",
  "transitionType": "next_exercise",
  "hint": "full_refresh"
}
Field Values
transitionType next_exercise, next_step, reveal_hint
hint full_refresh, partial_refresh, instant

5. Layering

Edge maintains three layers:

┌─────────────────────────────────┐
│      ANNOTATION LAYER           │  ← Highlights, marks, hints (top)
├─────────────────────────────────┤
│        STROKE LAYER             │  ← Student's writing (middle)
├─────────────────────────────────┤
│       CONTENT LAYER             │  ← Exercise content (bottom)
└─────────────────────────────────┘

Primitives specify which layer via their category:


6. Capability Adaptation

Not all Edges support all features. Bridge adapts primitives based on Edge capabilities.

6.1 Color Adaptation

When Edge reports color: false:

Primitive with intent Adaptation
HIGHLIGHT intent:error Hatched pattern fill
HIGHLIGHT intent:success Light solid fill
UNDERLINE intent:error Thicker line + wavy
MARK correct Standard checkmark (already works)

6.2 Animation Adaptation

When Edge reports refreshRate: slow (e-ink):

Primitive Adaptation
PULSE Single flash (invert region briefly)
TRANSITION hint:instant Ignored, use partial refresh

6.3 Size Adaptation

When Edge reports small screen:

Adaptation
HINT_BUBBLE text truncated, tap to expand
size: large items capped at medium

7. Message Format

7.1 Single Primitive

{
  "type": "PRIMITIVE",
  "primitive": {
    "type": "HIGHLIGHT",
    "id": "hl_001",
    ...
  }
}

7.2 Batch (Multiple Primitives)

{
  "type": "PRIMITIVE_BATCH",
  "primitives": [
    {"type": "HIGHLIGHT", "id": "hl_001", ...},
    {"type": "ARROW", "id": "arr_001", ...},
    {"type": "HINT_BUBBLE", "id": "hint_001", ...}
  ]
}

7.3 Exercise Load

Full exercise content sent as primitive batch:

{
  "type": "LOAD_EXERCISE",
  "exerciseId": "ex_4521",
  "primitives": [
    {"type": "TEXT", "id": "txt_001", "content": "Solve for x:", ...},
    {"type": "MATH_BLOCK", "id": "math_001", "content": "3x + 5 = 14", ...},
    {"type": "INPUT_ZONE", "id": "input_work", ...},
    {"type": "INPUT_ZONE", "id": "input_answer", ...},
    {"type": "DIVIDER", "id": "div_001", ...}
  ]
}

8. Examples

8.1 Simple Equation Exercise

{
  "type": "LOAD_EXERCISE",
  "exerciseId": "ex_linear_001",
  "primitives": [
    {
      "type": "TEXT",
      "id": "instruction",
      "content": "Solve for x:",
      "position": {"x": 0.05, "y": 0.05},
      "style": {"size": "medium", "semantic": "heading"}
    },
    {
      "type": "MATH_BLOCK",
      "id": "equation",
      "content": "3x + 5 = 14",
      "format": "asciimath",
      "position": {"x": 0.05, "y": 0.12},
      "size": "large"
    },
    {
      "type": "DIVIDER",
      "id": "div1",
      "y": 0.22,
      "inset": 0.05
    },
    {
      "type": "TEXT",
      "id": "work_label",
      "content": "Show your work:",
      "position": {"x": 0.05, "y": 0.25},
      "style": {"size": "small", "semantic": "label"}
    },
    {
      "type": "INPUT_ZONE",
      "id": "work_zone",
      "bounds": {"x": 0.05, "y": 0.30, "w": 0.9, "h": 0.35},
      "semantic": "work",
      "showBorder": false
    },
    {
      "type": "INPUT_GUIDE",
      "id": "work_guide",
      "bounds": {"x": 0.05, "y": 0.30, "w": 0.9, "h": 0.35},
      "guideType": "ruled",
      "spacing": 0.05
    },
    {
      "type": "DIVIDER",
      "id": "div2",
      "y": 0.68,
      "inset": 0.05
    },
    {
      "type": "TEXT",
      "id": "answer_label",
      "content": "x =",
      "position": {"x": 0.05, "y": 0.73},
      "style": {"size": "large", "weight": "bold"}
    },
    {
      "type": "INPUT_ZONE",
      "id": "answer_zone",
      "bounds": {"x": 0.15, "y": 0.70, "w": 0.25, "h": 0.08},
      "semantic": "answer",
      "showBorder": true
    }
  ]
}

8.2 Tutor Highlights an Error

{
  "type": "PRIMITIVE_BATCH",
  "primitives": [
    {
      "type": "HIGHLIGHT",
      "id": "error_hl",
      "bounds": {"x": 0.20, "y": 0.38, "w": 0.12, "h": 0.04},
      "intent": "error"
    },
    {
      "type": "UNDERLINE",
      "id": "error_ul",
      "from": {"x": 0.20, "y": 0.42},
      "to": {"x": 0.32, "y": 0.42},
      "intent": "error",
      "style": "wavy"
    },
    {
      "type": "ARROW",
      "id": "error_arrow",
      "from": {"x": 0.45, "y": 0.30},
      "to": {"x": 0.33, "y": 0.40},
      "intent": "error"
    },
    {
      "type": "HINT_BUBBLE",
      "id": "error_hint",
      "content": "Check the sign here",
      "anchor": {"x": 0.26, "y": 0.38},
      "position": "above"
    }
  ]
}

8.3 Correct Answer Feedback

{
  "type": "PRIMITIVE_BATCH",
  "primitives": [
    {
      "type": "HIGHLIGHT",
      "id": "success_hl",
      "bounds": {"x": 0.15, "y": 0.70, "w": 0.25, "h": 0.08},
      "intent": "success"
    },
    {
      "type": "MARK",
      "id": "checkmark",
      "position": {"x": 0.42, "y": 0.73},
      "markType": "correct",
      "size": "large"
    }
  ]
}

9. Open Questions

Question Options Notes
Math format preference AsciiMath vs LaTeX vs MathML AsciiMath is simpler, LaTeX more powerful
Hint bubble interactivity Tap to dismiss? Auto-dismiss? Affects Edge complexity
Primitive ID generation Bridge assigns vs Tutor assigns Bridge is closer to rendering
Sound primitives? BEEP, CHIME Or leave all audio to Tutor device
Haptics? VIBRATE primitive Some tablets support it

Appendix A: Intent Color Reference

Intent Hex (LCD) E-ink adaptation
focus #3B82F6 (blue) Gray 60% border
warning #F59E0B (amber) Dashed border
error #EF4444 (red) Thick border + hatch fill
success #22C55E (green) Light fill

Appendix B: Primitive Quick Reference

Category Primitives
Content TEXT, MATH_BLOCK, IMAGE, LINE, BOX, DIVIDER
Input INPUT_ZONE, INPUT_GUIDE
Annotation HIGHLIGHT, UNDERLINE, CIRCLE, ARROW, BRACKET, STRIKETHROUGH
Feedback MARK, PULSE, POINTER
Hint HINT_BUBBLE, HINT_POINTER
Control CLEAR, CLEAR_ID, TRANSITION

Next spec: Edge Contract (full interface specification)