Start here

The package that ships with this site

Everything below already exists in the unity/ folder alongside these HTML files. It is a plain folder rather than a .unitypackage so it diffs in git and carries no stale meta GUIDs — Unity writes those on first import.

unity/drop into Assets/
unity/
├── README.md                    full setup guide
├── Packages/manifest.json       AR Foundation, URP, Input System
└── Assets/SignalVerse/
    ├── Scripts/
    │   ├── Core/       AppState · SaveSystem · SignalVerseApp · ScreenRouter
    │   ├── Data/       PhaseDefinition · CurriculumDatabase · Achievement
    │   ├── AR/         ARSessionController · MarkerTracker
    │   │               WorkbenchAnchor · FaceLandmarkMapper
    │   ├── Signal/     SignalGenerator · WaveformRenderer
    │   ├── Learning/   PhaseManager · QuizController
    │   │               ExperimentController · Achievement/Certificate
    │   └── UI/         UIRoot · QuizScreen · WorkbenchHUD · Alert
    ├── UI/
    │   ├── Styles/     tokens.uss · components.uss
    │   └── Documents/  Workbench.uxml · Quiz.uxml
    ├── Editor/         CurriculumImporter · ReferenceImageLibraryBuilderGenerateMarkers.mjs
    ├── Resources/      curriculum.json
    └── Markers/        SV-M01…04.svg · print-sheet.html

Four commands and you are running

  1. Step 1

    New Unity 6000.0 LTS project from the Universal 3D template, then copy Assets/SignalVerse in.

  2. Step 2

    Merge Packages/manifest.json, reopen, let packages resolve.

  3. Step 3

    Tools ▸ Signal Verse ▸ Import Curriculum JSON — generates every PhaseDefinition and the CurriculumDatabase.

  4. Step 4

    Tools ▸ Signal Verse ▸ Build Reference Image Library — scans the markers and fixes their import settings.

One gotcha. The markers ship as SVG. Export them to PNG at 1024 × 1024 into the same folder first — Unity will not import SVG without the Vector Graphics package.

Architecture

Three rules that keep it debuggable

01

One writer per concern

PhaseManager is the only class that writes to PhaseProgress. SignalGenerator is the only source of the current wave — the renderer, the readouts and the experiment all read from it rather than keeping a copy. “Why did Phase 3 unlock?” has exactly one place to look.

02

State flows one way

Mutate SignalVerseApp.Instance.State, call MarkDirty(), and every subscriber to StateChanged re-renders. Writes are debounced 1.5 s and flushed on pause and quit. SaveSystem writes to a temp file and moves it, so a crash mid-save cannot corrupt the save.

03

Screens are factories

ScreenRouter holds a stack with the same semantics as UINavigationController. Register a factory per screen name, then call Go, Back, Replace, Root or Present. Each mode maps to a USS class pair.

Scripts/Core/ScreenRouter.csC#
// Registration happens once, in the Boot scene.
router.Register("quiz", args => new QuizScreen(quizTree, quizController, ResolvePhase(args)));
router.Register("workbench", args => new WorkbenchScreen(workbenchTree, ResolvePhase(args)));

// Navigation reads the same everywhere in the app.
router.Go("phase-intro", phase.phaseId);
router.Present("scanner");
router.Replace("quiz-result", outcome, Transition.Fade);
router.BackTo("tab-home");

Scenes

Boot, AR, and one prefab

Three scenes total. Boot is the only one in the build's first slot; it carries the services and survives every load.

Boot.unityservices + UI root
SignalVerseApp        SignalVerseApp.cs  ← CurriculumDatabase
                      PhaseManager.cs
                      AchievementService.cs
                      CertificateService.cs
UI                    UIDocument            PanelSettings
                      UIRoot.cs
                      ScreenRouter.cs
EventSystem           InputSystemUIInputModule
AR.unitytracking
XR Origin
 ├─ Camera Offset
 │   └─ Main Camera    ARCameraManager
 │                     ARCameraBackground
 ├─ AR Plane Manager
 ├─ AR Raycast Manager
 ├─ AR Anchor Manager
 └─ AR Tracked Image Manager
       Library: SignalVerseMarkers
       Max Moving Images: 1
AR Session           ARSessionController.cs
AR Services          MarkerTracker.cs
                      WorkbenchAnchor.cs
Workbench HUD        UIDocument (Workbench.uxml)
                      WorkbenchHUD.cs

PanelSettings — the one setting everything depends on

Scale mode Scale With Screen Size, reference resolution 440 × 956, match 0.5. That makes 1 px in USS equal 1 pt on device, which is the entire reason tokens.uss can copy the website's numbers verbatim instead of maintaining a second scale.

Design tokens

CSS custom properties become USS variables

The token names are identical on both sides. If you rename one, rename it in both files — Tools ▸ Signal Verse ▸ Validate Design Tokens diffs them and reports drift.

Mapping from website CSS to Unity USS
Website (CSS)Unity (USS)Note
--accent: #0A84FF--app-accent: #0A84FFIdentical value
--space-4: 1rem--space-4: 16rem → px, 1 px = 1 pt
--r-xl: 28px--r-xl: 20Device radii are tighter than web
--shadow-3background-image: Shadow_CardNo box-shadow in USS
backdrop-filter: blur()Blur pass + semi-opaque fillNo backdrop filter in USS
cubic-bezier(.22,1,.36,1)ease-out-circClosest named curve Unity ships
@media (prefers-color-scheme).theme-dark on rootUSS has no media queries
@media (prefers-reduced-motion).motion-reduced on rootDurations collapse, callbacks still fire
assets/css/tokens.cssweb
:root {
  --accent: #0A84FF;
  --space-4: 1rem;
  --r-lg: 20px;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}
UI/Styles/tokens.ussUnity
:root {
  --app-accent: #0A84FF;
  --space-4: 16;
  --r-lg: 16;
  /* ease-out-circ ≈ the web curve */
}

Physical size

80 mm

Library entry

SV-M01

Markers

Why these are not ArUco codes

AR Foundation does natural feature tracking, not fiducial decoding. A plain black-and-white grid scores badly in the Reference Image Library because its features repeat and its corners are ambiguous.

The shipped markers are built for the tracker instead: an asymmetric block field, a waveform stroke that differs per phase, large glyphs — text is very feature-rich — one solid corner block so orientation is unambiguous, and a white quiet zone so the detector can find the boundary.

Print at 100%

Outer brackets must measure 80 mm. That number is PhysicalSizeMetres in ReferenceImageLibraryBuilder.cs. A scaled print makes every distance the workbench reports wrong — MarkerTracker checks the measured size against the library and warns in the console rather than failing silently.

regenerate the artworknode
node unity/Assets/SignalVerse/Editor/GenerateMarkers.mjs

Rendering

One generator, many consumers

SignalGenerator.Sample() is pure and allocation-free, so it is safe to call per-vertex, per-frame, from the renderer, the readouts and the experiment at once.

Scripts/Signal/WaveformRenderer.csC#
void LateUpdate()
{
    if (generator == null || _points == null) return;

    float halfWidth = width * 0.5f;
    float halfHeight = height * 0.5f;
    float time = Time.time;

    for (int i = 0; i < _points.Length; i++)
    {
        float x01 = i / (float)(_points.Length - 1);
        float y = generator.Sample(x01, time);

        _points[i] = new Vector3(
            Mathf.Lerp(-halfWidth, halfWidth, x01),
            y * halfHeight,
            0f);
    }

    _line.SetPositions(_points);   // one native call, zero garbage

    // Turn the trace red the moment the learner drives it into clipping.
    _line.startColor = _line.endColor = generator.IsClipping ? clippingColour : _baseColour;
}

Vertices per trace

192

Draw calls, workbench visible

<40

Marker → anchored

1.2 s

Frame budget

16.6 ms

Conversion path

Prototype file → Unity file

The browser prototype was written so this mapping is one-to-one. Nothing in it is a throwaway — each module has a named destination.

Mapping from prototype modules to Unity scripts
Prototype (JavaScript)Unity (C#)What carries over
prototype/state.jsCore/AppState.cs + SaveSystem.csShape, defaults, migration hook
prototype/router.jsCore/ScreenRouter.csStack semantics, six transitions
prototype/screens/*.jsUI/*Screen.csLayout, copy, interaction order
prototype/components.jsUI/Styles/components.ussEvery class name
modules/canvas.jscreateWaveSignal/SignalGenerator.csThe five sampling functions, verbatim
data/curriculum.jsResources/curriculum.jsonGenerated from the same source
assets/css/tokens.cssUI/Styles/tokens.ussColour, type, spacing, motion

QuizScreen.cs is the worked example. Copy its shape for the remaining screens: query elements once in BuildUI, subscribe in OnMount, unsubscribe in OnUnmount.

Before you ship

Three checklists

Performance

  • No Vector3[] allocated inside LateUpdate
  • No world-space UI canvases — project to screen space instead
  • AR session stopped when leaving the AR screens
  • 4× MSAA on — the trace crawls without it
  • IL2CPP + ARM64 only on Android

Accessibility

  • Every AR readout mirrored as a text region screen readers can reach
  • .text-large path verified on every screen
  • .motion-reduced collapses durations but keeps callbacks firing
  • Touch targets at least 44 × 44 pt
  • Quiz answerable without relying on colour alone

Privacy & store

  • Camera usage string written in plain language
  • No camera frame written to disk or sent anywhere
  • Phase 3 face mesh computed and discarded in the same frame
  • Export and erase paths both reachable from Settings
  • Native share plugin added before submitting

Design system next

Every token, component state and motion rule this handoff refers to, documented in full.