Runtime
Building a Web-Based 3D Cyber Kill Chain Simulation with Virtual Reality Support
Report summary
A credible cyber kill chain simulator should be designed as an instructional, event-driven digital environment , not as an attack-execution platform. Its purpose is to let learners observe how adversary behavior progresses, identify indicators, make defensive decisions, and inspect the consequences
Key topics
- Runtime
- AI
- TypeScript
- Angular
- RxJS
- Privacy
- Physics
- Semantic Systems
Research provenance
For citation, use the report title and canonical URL. Archival presence does not establish authorship or promote report statements into portfolio evidence.
Source availability: 46 citation markers in the source export have no recoverable source links. Those markers are omitted from this reader; any supplied bibliography and ordinary links remain. Check the original sources before relying on the cited claims.
This page renders the archived Markdown as safe, formatted HTML. It is background research and does not become a portfolio claim without evidence review.
Full report
On this page
Executive summary
A credible cyber kill chain simulator should be designed as an instructional, event-driven digital environment, not as an attack-execution platform. Its purpose is to let learners observe how adversary behavior progresses, identify indicators, make defensive decisions, and inspect the consequences of those decisions across a spatialized timeline or network. The safest and most maintainable implementation separates a PHP control plane from a JavaScript visualization and interaction plane.
The recommended baseline is:
| Layer | Recommended baseline | Rationale |
|---|---|---|
| Browser application | TypeScript, three.js r185, WebXR, Vite, and either React 19.2 or Vue 3 | three.js provides direct control over rendering, WebXR interaction, scene optimization, and custom instructional visualizations without the abstraction ceiling of a declarative engine. three.js r185 was current as of July 2026. |
| PHP application | PHP 8.5 on the latest security patch, with Laravel 13 for delivery speed or Symfony 7.4 LTS for longer support | PHP 8.5 is the current supported branch; Laravel provides rapid API, authentication, queue, rate-limit, and broadcasting integration, while Symfony 7.4 is the more conservative long-term-support choice. |
| Persistence | PostgreSQL plus JSONB; Redis for cache, queues, presence, and fan-out | The domain contains strongly relational objects—runs, stages, actors, participants—and evolving event payloads that benefit from JSON documents. |
| Real-time transport | REST for resources and commands, WebSocket for live state, SSE as a simpler one-way fallback | WebSocket supports bidirectional browser/server communication; SSE provides reconnectable server-to-browser delivery and is appropriate for guided playback or instructor feeds. |
| Asset delivery | glTF/GLB, KTX2 textures, object storage, and a CDN | glTF is intended as an efficient runtime asset-delivery format; KTX2/Basis compression reduces texture transfer and GPU memory pressure. |
| Cyber-content model | Lockheed Martin Cyber Kill Chain for the narrative sequence; MITRE ATT&CK for behavior-level annotation | The kill chain supplies an understandable ordered storyline, while ATT&CK supplies a more granular, empirically curated taxonomy. MITRE explicitly describes the two as complementary rather than interchangeable. |
| Observability | OpenTelemetry-compatible logs, spans, and semantic events | OpenTelemetry distinguishes duration-bearing spans from point-in-time events and provides a vendor-neutral telemetry model. |
The simulation should maintain an authoritative server-side event stream. Clients send constrained instructional commands such as advance_stage, inspect_indicator, or apply_control; the PHP service checks authorization and scenario rules, appends an immutable event, and broadcasts the resulting state delta. A client must never be allowed to submit scripts, shell commands, arbitrary URLs, executable malware, or instructions to contact real systems.
A useful first release is a single-user, synthetic phishing-to-impact scenario that supports both desktop and headset use, seven Lockheed stages, ATT&CK annotations, guided instruction, two or three intervention points, deterministic replay, basic learner metrics, and a small instructor dashboard. A reasonable planning estimate is 12–16 person-weeks, corresponding to approximately six to eight calendar weeks for two engineers with part-time involvement from a cybersecurity subject-matter expert and an instructional or UX designer. Multiuser synchronization, voice, scenario authoring, enterprise identity, and formal learning evaluation should follow after the core event model has stabilized.
The principal architectural risk is not rendering. It is allowing instructional scenario content, real-time messages, uploaded models, or facilitator privileges to become an execution or injection channel. The principal pedagogical risk is producing an impressive spatial spectacle that overloads learners without improving causal understanding. Immersive-learning research indicates that presence and agency can increase engagement, but also that visual complexity and unguided autonomy can increase extraneous cognitive load; reflection prompts and instructional scaffolding are therefore essential.
Scope, assumptions, threat model, and ethical boundaries
Unspecified assumptions. No hosting provider, database engine, identity provider, headset family, classroom size, or regulatory jurisdiction was specified. This report therefore assumes a browser-accessible deployment over HTTPS; modern WebXR-capable headsets; desktop fallback for users without headsets; PostgreSQL as a reference database rather than a mandatory dependency; object storage for 3D assets; and an initial concurrency target in the tens or low hundreds of users rather than a globally distributed consumer game. Estimates assume that 3D models are stylized and reusable rather than custom photorealistic productions.
The system should represent adversary behavior at the level needed to teach recognition, causality, detection, and defensive response. It should not contain operational exploit chains, functional payloads, credential-harvesting infrastructure, arbitrary command execution, malware deployment, public command-and-control endpoints, or automation against third-party networks. MITRE ATT&CK can support threat modeling, defensive assessment, red-team planning, and adversary emulation, but the simulation should consume ATT&CK as a taxonomy and content reference—not turn ATT&CK procedures into directly executable abilities.
Threat model
| Threat actor or failure mode | Assets at risk | Representative abuse case | Required controls |
|---|---|---|---|
| Malicious unauthenticated user | Availability, public assets, account endpoints | API enumeration, credential stuffing, resource exhaustion, oversized model downloads | Authentication, bot protection where appropriate, per-route rate limits, CDN controls, request-size limits, generic error responses |
| Malicious learner | Run integrity, other learners’ data, facilitator controls | Sending forged stage transitions, reading another class’s run, replaying commands, injecting labels or URLs | Object-level authorization on every run, membership checks, idempotency keys, sequence validation, strict JSON schemas, output encoding |
| Compromised facilitator account | Scenario integrity, participant telemetry, administrative functions | Modifying scenarios, revealing answers, exporting learner data, launching unauthorized sessions | Strong authentication, short sessions, least privilege, step-up authentication for exports, immutable audit records |
| Malicious scenario author | Browser security and ethical boundary | Uploading script-bearing content, remote textures, hostile glTF extensions, realistic exploit instructions | Content security policy, asset transcoding and quarantine, extension allowlists, no executable fields, editorial review, publishing workflow |
| Realtime protocol attacker | Run integrity and availability | Cross-site WebSocket hijacking, message flooding, sequence replay, oversized frames | Origin validation, authenticated upgrade, per-connection quotas, message-size caps, heartbeat timeouts, monotonic sequence numbers |
| Dependency or build compromise | Application code and learner devices | Compromised npm or Composer dependency, modified 3D asset, poisoned build artifact | Lockfiles, pinned versions, software bills of materials, signed builds, dependency scanning, reproducible deployment pipeline |
| Over-privileged telemetry operator | Learner privacy | Reconstructing identity or behavior from head, hand, gaze, voice, or room data | Data minimization, pseudonymous identifiers, short retention, aggregation, access logging, explicit consent where required |
| Denial-of-service condition | PHP workers, database, WebSocket gateway, GPU memory | Reconnect storms, telemetry floods, high-poly asset exhaustion, adversarial scene complexity | Backpressure, circuit breakers, queue limits, asset budgets, scene validators, degraded read-only mode |
This threat model should be revisited whenever the product adds user-generated scenarios, live cyber-range integrations, voice, gaze tracking, artificial-intelligence-generated content, or cross-organization collaboration. Those additions materially change both the attack surface and the legal basis for data processing.
Ethical and legal guardrails
In the United States, the Computer Fraud and Abuse Act addresses unauthorized access and damage involving protected computers. A training environment should therefore define explicit authorization boundaries and ensure that neither client nor server can initiate actions against systems outside the controlled simulation or a separately authorized cyber range. Written scope, approved targets, permitted actions, dates, contacts, and stop conditions should be established for any lab integration.
CISA’s vulnerability-disclosure policy template illustrates the importance of clearly specifying which systems and conduct are authorized. A comparable simulation policy should state that scenarios are fictional or sanitized, external probing is prohibited, exported material remains non-operational, and users must not substitute real targets, credentials, domains, or malware.
Where learner telemetry constitutes personal data, privacy law may require a lawful basis, transparency, purpose limitation, minimization, retention limits, and rights-handling processes. The European Union’s GDPR is the obvious example, although education, employment, biometric, child-privacy, state, and sector-specific laws may impose additional requirements. Head pose, hand movement, gaze, voice, accessibility settings, and performance history can be more identifying or sensitive than conventional clickstream data and should not be collected merely because an XR API makes collection possible.
Recommended non-negotiable content controls are:
- Use reserved domains such as
.invalid, synthetic IP ranges, fictional identities, and nonfunctional hashes. - Store descriptive ATT&CK references, defensive observations, and abstract effects rather than executable commands.
- Prevent scenario authors from adding JavaScript, shell syntax, executable attachments, arbitrary HTML, external model URLs, or unreviewed WebRTC endpoints.
- Require a two-person review for scenarios marked realistic, organization-specific, exportable, or linked to a cyber range.
- Display an instructional-use notice and a visible distinction between simulated evidence and real indicators.
- Treat ATT&CK names, logos, and attribution according to MITRE’s legal and branding guidance.
Canonical models and simulation-stage mapping
The 2011 Hutchins, Cloppert, and Amin paper introduced an intrusion kill chain centered on seven phases: reconnaissance, weaponization, delivery, exploitation, installation, command and control, and actions on objectives. Its defensive value is the proposition that disrupting one or more links can prevent an intrusion from reaching its objective, while observations from each campaign can improve later defenses.
MITRE ATT&CK is structured differently. Tactics describe adversary objectives, while techniques and sub-techniques describe behaviors used to accomplish those objectives. MITRE’s design paper characterizes ATT&CK as a mid-level, empirically grounded model based substantially on publicly reported behavior, and cautions against treating coverage as a checklist or inferring attribution from techniques alone.
A simulation should therefore use:
- Lockheed stages as the learner-facing narrative spine.
- ATT&CK tactics and techniques as annotations, branching behavior, evidence, and assessment labels.
- Scenario-specific states for detection, containment, recovery, and reflection, which are not merely adversary stages.
- Explicit version pinning so that a scenario does not silently change when the ATT&CK knowledge base changes.
ATT&CK v19.1 was the current content version on July 31, 2026. MITRE provides current and historical data through STIX and TAXII, so each published scenario should record the ATT&CK release and object versions against which it was authored.
Recommended crosswalk
The following is a teaching-oriented crosswalk, not a one-to-one ontology. MITRE notes that ATT&CK tactics are lower-level objectives and are not constrained to a single linear order, whereas a kill chain is ordered. One ATT&CK behavior may appear in several narrative stages, and later tactics may loop back or occur concurrently.
| Lockheed stage | Representative ATT&CK mapping | Suggested 3D representation | Learner objective |
|---|---|---|---|
| Reconnaissance | Reconnaissance, such as gathering target and infrastructure information | External observation zone around a protected enterprise; public-facing assets illuminate as information is gathered | Identify exposed information, distinguish benign observation from suspicious collection, select exposure-reduction controls |
| Weaponization | Resource Development, including acquiring infrastructure, accounts, or capabilities | An abstract “assembly” area that links capability, infrastructure, lure, and intended victim; no executable artifacts | Understand that campaigns require preparation and that defenders may detect infrastructure or impersonation before delivery |
| Delivery | Initial Access, especially phishing or trusted-relationship paths | A message, removable medium, web request, or supply-chain object moving across a boundary | Recognize ingress channels, inspect evidence, choose filtering, verification, or isolation controls |
| Exploitation | Initial Access plus Execution; sometimes Privilege Escalation | A vulnerable service or user action produces a visible state transition, process node, or privilege boundary change | Connect vulnerability, user action, execution, and control failure without exposing functional exploit code |
| Installation | Persistence and Defense Evasion | Persistent node or hidden path appears; defensive visibility changes | Identify persistence, tampering, suspicious services, and opportunities for host isolation or restoration |
| Command and Control | Command and Control | Periodic beacon-like edges to a fictional external controller, with timing and protocol evidence | Distinguish normal traffic from anomalous communication and select blocking, sinkholing, or segmentation responses |
| Actions on Objectives | Discovery, Credential Access, Lateral Movement, Collection, Exfiltration, and Impact | Branching paths across identity, data, and service zones; consequences depend on prior interventions | Reason about blast radius, protect objectives, prioritize containment, preserve evidence, and initiate recovery |
The stage engine should support directed graphs rather than a rigid seven-element array. The primary scenario can still display seven numbered narrative locations, but its internal state should permit concurrent actions, loops, aborted attempts, skipped stages, alternate delivery paths, defender interventions, and recovery branches. A useful state-transition record contains:
current stage + preconditions + adversary event + evidence generated
+ learner decision + defensive control + probabilistic/deterministic outcome
+ next-stage candidates + assessment feedback
For teaching clarity, separate three synchronized timelines:
- Adversary timeline: what the simulated actor attempted.
- Observable timeline: what logs, alerts, indicators, or user reports became available.
- Defender timeline: what the learner or simulated security team knew and did.
This separation avoids teaching the unrealistic assumption that defenders see the attacker’s ground truth in real time. It also supports exercises in which a learner must distinguish evidence from hindsight.
Data model, schemas, and event semantics
The recommended persistence model combines a relational scenario catalog with an append-only run event stream. Relational constraints protect identities, membership, ordering, and publication state; JSONB accommodates evolving scenario configuration and event-specific evidence. The browser should receive a purpose-built simulation representation rather than an unrestricted copy of the database.
STIX 2.1 is useful at the interchange boundary because it provides a standardized language for cyber-threat intelligence, and MITRE distributes ATT&CK as STIX 2.0 and 2.1. The internal simulation schema should not simply duplicate STIX: instructional concepts such as learner prompts, spatial transforms, reveal conditions, scores, comfort settings, and replay sequence numbers are outside its principal purpose. Store STIX identifiers and selected normalized relationships while retaining a simulation-specific model.
OpenTelemetry should similarly be treated as an observability envelope rather than the authoritative scenario domain. Use spans for operations with duration—asset loading, scenario initialization, API processing—and events or logs for point-in-time occurrences such as stage transitions or interaction failures.
Reference relational schema
| Table | Important fields | Purpose and constraints |
|---|---|---|
users | id UUID, auth_subject, email, display_name, status, created_at | Local identity projection. auth_subject should be unique; sensitive profile data should remain in the identity provider where possible. |
roles and user_roles | role_code, user_id, scope_type, scope_id | Supports global and scenario/run-scoped roles such as learner, facilitator, author, reviewer, and administrator. |
scenarios | id, slug, title, version, status, attack_content_version, threat_model JSONB, settings JSONB, created_by, timestamps | Immutable published versions are preferable. Editing should create a draft successor rather than mutate a scenario used by completed runs. |
stages | id, scenario_id, code, ordinal, lockheed_stage, title, description, entry_conditions JSONB, completion_conditions JSONB, spatial_anchor JSONB | Defines learner-facing narrative stages. ordinal is for display, not the only transition rule. |
stage_attack_refs | stage_id, attack_object_id, object_type, object_version, relationship_type | Pins tactics, techniques, mitigations, or other ATT&CK objects to a scenario version. |
stage_transitions | id, from_stage_id, to_stage_id, trigger_type, condition JSONB, probability, priority | Supports branches, loops, interventions, and alternate paths. Deterministic educational scenarios can use probability 1.0. |
actors | id, scenario_id, actor_type, name, trust_zone, metadata JSONB | Represents fictional adversaries, defenders, users, services, and automated agents. Avoid unsupported real-world attribution. |
entities | id, scenario_id, entity_type, name, asset_uri, transform JSONB, properties JSONB | Spatial and logical objects such as hosts, identities, messages, controls, data stores, and network zones. |
indicators | id, scenario_id, indicator_type, display_value, normalized_hash, is_synthetic, stix_id, sensitivity, metadata JSONB | Store redacted or hashed values when full values are unnecessary. Enforce is_synthetic for distributable scenarios. |
runs | id, scenario_id, scenario_version, mode, status, facilitator_id, seed, current_seq, started_at, ended_at | Runtime aggregate and current sequence. seed enables deterministic probabilistic replay. |
run_participants | run_id, user_id, run_role, joined_at, last_seen_at, consent_version | Authoritative membership and telemetry-consent reference. |
simulation_events | id, run_id, seq, event_type, stage_id, actor_id, entity_id, occurred_at, received_at, payload JSONB, visibility, correlation_id, causation_id, integrity_hash | Append-only source of truth. Unique constraint on (run_id, seq) and preferably (run_id, idempotency_key). |
run_snapshots | run_id, through_seq, state JSONB, created_at, schema_version | Reduces replay time. A snapshot is derived data and can be rebuilt from events. |
learner_responses | id, run_id, participant_id, prompt_id, response JSONB, correctness, latency_ms, event_seq | Keeps assessment records linked to the exact scenario state. |
telemetry_events | id, run_id, participant_id, event_name, occurred_at, duration_ms, attributes JSONB, retention_until | Operational and learning telemetry. Do not place raw pose streams here by default. |
audit_log | id, principal_id, action, resource_type, resource_id, result, ip_hash, user_agent_hash, occurred_at | Security and governance evidence. Protect from normal application modification. |
Sample ER diagram
erDiagram
USERS ||--o{ USER_ROLES : receives
ROLES ||--o{ USER_ROLES : defines
USERS ||--o{ SCENARIOS : authors
USERS ||--o{ RUNS : facilitates
SCENARIOS ||--|{ STAGES : contains
SCENARIOS ||--o{ ACTORS : defines
SCENARIOS ||--o{ ENTITIES : contains
SCENARIOS ||--o{ INDICATORS : contains
STAGES ||--o{ STAGE_ATTACK_REFS : maps_to
STAGES ||--o{ STAGE_TRANSITIONS : originates
STAGES ||--o{ STAGE_TRANSITIONS : targets
SCENARIOS ||--o{ RUNS : instantiates
RUNS ||--o{ RUN_PARTICIPANTS : includes
USERS ||--o{ RUN_PARTICIPANTS : joins
RUNS ||--|{ SIMULATION_EVENTS : records
RUNS ||--o{ RUN_SNAPSHOTS : snapshots
RUNS ||--o{ LEARNER_RESPONSES : assesses
RUNS ||--o{ TELEMETRY_EVENTS : measures
STAGES ||--o{ SIMULATION_EVENTS : contextualizes
ACTORS ||--o{ SIMULATION_EVENTS : performs
ENTITIES ||--o{ SIMULATION_EVENTS : affects
USERS ||--o{ AUDIT_LOG : generates
Canonical event envelope
All runtime events should share a stable envelope. Event-specific data belongs under payload, and schemas should be versioned independently of scenario content.
{
"schema_version": "1.0",
"event_id": "019bf87e-c03b-7ce4-a81d-27f68ed31220",
"run_id": "019bf85a-a54d-70fe-a63a-d68af741f028",
"sequence": 42,
"event_type": "indicator.observed",
"occurred_at": "2026-07-31T19:42:17.431Z",
"correlation_id": "019bf877-d10e-7acd-949a-e337e4d45eb7",
"causation_id": "019bf87b-2c30-78c2-97af-cbe901d39e92",
"stage": {
"code": "delivery",
"lockheed_stage": "delivery",
"attack_content_version": "19.1",
"attack_tactics": ["TA0001"],
"attack_techniques": ["T1566.002"]
},
"actor_ref": "actor:fictional-red-team",
"target_ref": "entity:mail-gateway",
"payload": {
"indicator_type": "url",
"display_value": "hxxps://benefits-update.training.invalid/",
"is_synthetic": true,
"confidence": 0.95,
"evidence_refs": [
"evidence:mail-header-017",
"evidence:proxy-log-009"
],
"available_defensive_actions": [
"quarantine_message",
"block_synthetic_domain",
"notify_recipient"
]
},
"visibility": {
"learner": true,
"facilitator": true,
"adversary_ground_truth": false
},
"integrity": {
"previous_event_hash": "sha256:3d76...f05a",
"event_hash": "sha256:b40c...a277"
}
}
The sequence controls replay and gap detection; it is not a global timestamp. occurred_at records simulated or real runtime time, while the database should separately retain receipt time. The integrity chain is optional but useful for assessed exercises. It is not a substitute for database access control or signed audit storage.
API surface
| Method and path | Purpose | Important behavior |
|---|---|---|
GET /api/v1/scenarios/{scenario} | Retrieve published metadata and stage graph | Exclude facilitator-only solutions and unrevealed evidence |
POST /api/v1/runs | Create a run from a fixed scenario version | Require facilitator or self-study permission; capture mode and deterministic seed |
POST /api/v1/runs/{run}/join | Join an authorized run | Return participant role, snapshot URL, last sequence, and realtime ticket |
GET /api/v1/runs/{run}/snapshot | Retrieve current materialized state | Include through_seq; support ETag and compressed responses |
GET /api/v1/runs/{run}/events?after_seq=42 | Recover missed deltas | Enforce run membership and visibility filtering |
POST /api/v1/runs/{run}/commands | Submit constrained learner or facilitator intent | Require idempotency key; return accepted sequence or rule violation |
POST /api/v1/telemetry/batch | Submit privacy-filtered telemetry | Size, event-count, attribute, and rate limits; reject unrecognized dimensions |
GET /api/v1/runs/{run}/metrics | Retrieve authorized summaries | Learners see their metrics; facilitators see approved cohort aggregates |
WS /ws/runs/{run} | Receive state deltas, presence, and facilitator messages | Authenticate the upgrade and authorize every subscription |
Architecture, integration patterns, and core flows
Reference architecture
flowchart LR
subgraph Browser["JavaScript / TypeScript browser client"]
UI["React, Vue, or Angular UI shell"]
Scene["three.js / Babylon.js / A-Frame scene"]
XR["WebXR input and session manager"]
Store["Client state store and replay reducer"]
Cache["IndexedDB and asset cache"]
Worker["Web Worker for event processing"]
UI <--> Store
Scene <--> Store
XR --> Scene
Worker <--> Store
Cache <--> Store
end
subgraph Edge["Edge services"]
CDN["CDN / static asset delivery"]
Proxy["TLS reverse proxy / WAF"]
end
subgraph PHP["PHP application"]
API["REST or GraphQL API"]
Auth["Authentication and authorization"]
Domain["Scenario and run domain services"]
Ingest["Telemetry ingestion"]
Queue["Background jobs"]
Audit["Audit service"]
Outbox["Transactional outbox"]
end
subgraph Realtime["Realtime tier"]
WS["WebSocket gateway"]
SSE["Optional SSE endpoint"]
Bus["Redis streams / pub-sub"]
end
subgraph Data["Data services"]
DB["PostgreSQL"]
Redis["Redis cache / presence"]
Objects["Object storage: GLB, KTX2, audio"]
OTel["OpenTelemetry collector"]
end
Browser -->|HTTPS REST / GraphQL| Proxy
Proxy --> API
Browser <-->|WSS deltas and commands| WS
Browser -->|Assets| CDN
CDN --> Objects
API --> Auth
API --> Domain
API --> Ingest
Domain --> DB
Domain --> Outbox
Outbox --> Bus
Bus --> WS
Bus --> SSE
WS --> Redis
Queue --> DB
Ingest --> OTel
Audit --> DB
Server-side PHP responsibilities
The PHP application should own security and domain authority:
- Authentication integration, account projection, run membership, RBAC, and resource-level authorization.
- Scenario publication, schema validation, ATT&CK version pinning, and content review state.
- Run creation, transition-rule evaluation, random-seed control, scoring, and authoritative sequence assignment.
- Durable append-only event persistence and snapshot creation.
- Signed or authorized asset manifests, not direct arbitrary storage paths.
- Telemetry filtering, retention enforcement, aggregation, and export authorization.
- Audit logs, administrative workflows, queues, notifications, and integration adapters.
- Generation of short-lived WebSocket subscription tickets where the realtime tier is separate.
Laravel 13 includes first-party broadcasting integration and can use Laravel Reverb for WebSocket communication; its rate-limiting abstraction also fits command and telemetry endpoints. Laravel Octane can run on long-lived application servers when higher PHP throughput is necessary, although long-lived workers require careful avoidance of request-state leakage.
Client-side JavaScript responsibilities
The browser should own presentation and immediate interaction, but not authority:
- WebGL scene construction, rendering, animation, spatial audio, label management, and adaptive quality.
- WebXR session lifecycle, controller/hand input, ray interaction, teleportation, snap turning, and seated mode.
- Local interpolation and prediction for presentation-only movement.
- Deterministic reduction of server events into local scene state.
- Gap detection, reconnection, snapshot recovery, and offline asset caching.
- Accessibility and desktop parity.
- Privacy-preserving derivation of coarse interaction metrics before transmission.
- Visual distinction between adversary ground truth, defender observations, instructional overlays, and learner decisions.
WebXR defines browser interfaces for immersive sessions, poses, views, and input; it is the standards layer rather than a scene engine. The current WebXR Device API was a Candidate Recommendation Draft in June 2026, so applications should feature-detect capabilities rather than infer them from headset names.
Integration-pattern comparison
| Pattern | Best use | Advantages | Limitations | Recommendation |
|---|---|---|---|---|
| REST | Scenario resources, run lifecycle, commands, snapshots, exports | Straightforward authorization, HTTP caching, mature PHP tooling, easy observability | Overfetching can occur; polling is inefficient for live state | Make this the default control-plane interface |
| GraphQL | Complex authoring tools, filtered scenario catalogs, analytics dashboards | Client-selected fields, typed schema, convenient aggregation across related resources | Field-level authorization and query-cost control are demanding; subscriptions add infrastructure complexity | Optional for administrative and authoring surfaces, not necessary for the MVP |
| WebSocket | Multiuser state, facilitator control, presence, live event deltas | Full-duplex, low-overhead messages after upgrade, natural fit for live sessions | Persistent connection operations, backpressure, authorization, origin checks, reconnect, and fan-out are nontrivial | Preferred realtime channel after the single-user prototype |
| Server-Sent Events | Guided playback, instructor announcements, read-only event streams | Simple HTTP semantics, automatic reconnect behavior, Last-Event-ID recovery, proxy-friendly | Server-to-client only; commands still require HTTP | Strong MVP option where participants mostly receive state |
| WebRTC | Voice, optional video, or peer media/data experiments | Browser-native real-time media and peer communication | Requires signaling and network traversal infrastructure; peer state is harder to audit and govern | Use for voice or media only, not as the authoritative simulation protocol |
| Batch upload | Telemetry, delayed metrics, device diagnostics | Reduces request overhead and avoids per-frame traffic | Data may be delayed or lost on abrupt exit | Send small bounded batches with sendBeacon or normal authenticated POST |
GraphQL’s current specification was published in September 2025. WebSocket is standardized by RFC 6455; SSE’s EventSource model includes automatic reconnection and last-event identifiers; WebRTC is a W3C Recommendation for browser real-time media communication.
Authoritative state synchronization
A robust synchronization sequence is:
- The client retrieves a snapshot with
through_seq = N. - The client opens the realtime connection and requests events after
N. - A learner interaction produces a command, not a claimed state transition.
- The PHP domain service verifies membership, role, current run version, command schema, idempotency key, and transition preconditions.
- Within one database transaction, the service assigns
N+1, writes the event, updates the run sequence, and writes an outbox record. - An outbox consumer publishes the committed event to the realtime bus.
- Clients apply events in sequence order. A gap triggers
GET .../events?after_seq=...; a large gap or schema mismatch triggers a fresh snapshot. - Periodic snapshots make long runs inexpensive to resume.
Client interpolation may smooth moving avatars or animated packets, but learning-critical values—stage, score, evidence visibility, action outcome, timing, and completion—must remain server-authoritative. Never send state at the display refresh rate. Broadcast semantic changes and low-frequency participant poses separately.
PHP endpoint example
The following Laravel-style endpoint accepts only allowlisted instructional commands. Authentication middleware, policy checks, an idempotency key, transactional locking, and after-commit broadcasting prevent the client from directly choosing authoritative state.
<?php
use App\Http\Controllers\RunCommandController;
use Illuminate\Support\Facades\Route;
Route::middleware(['auth:sanctum', 'throttle:simulation-commands'])
->post('/v1/runs/{run}/commands', [RunCommandController::class, 'store']);
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Events\SimulationEventCommitted;
use App\Models\Run;
use App\Models\SimulationEvent;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
final class RunCommandController extends Controller
{
public function store(Request $request, Run $run): JsonResponse
{
Gate::authorize('submitCommand', $run);
$data = $request->validate([
'command_id' => ['required', 'uuid'],
'type' => [
'required',
Rule::in([
'advance_stage',
'inspect_indicator',
'apply_control',
'answer_prompt',
'acknowledge_briefing',
]),
],
'expected_sequence' => ['required', 'integer', 'min:0'],
'payload' => ['present', 'array'],
]);
$event = DB::transaction(function () use ($run, $data): SimulationEvent {
/** @var Run $lockedRun */
$lockedRun = Run::query()
->whereKey($run->getKey())
->lockForUpdate()
->firstOrFail();
$existing = SimulationEvent::query()
->where('run_id', $lockedRun->id)
->where('idempotency_key', $data['command_id'])
->first();
if ($existing !== null) {
return $existing;
}
abort_if(
$lockedRun->current_seq !== $data['expected_sequence'],
409,
'The run has advanced; refresh state and retry.'
);
// The domain service should validate scenario rules and convert the
// command into a permitted event. It must never execute payload text.
$result = app(\App\Domain\Simulation\CommandHandler::class)
->handle($lockedRun, $data['type'], $data['payload']);
$nextSequence = $lockedRun->current_seq + 1;
$event = SimulationEvent::query()->create([
'id' => (string) Str::uuid7(),
'run_id' => $lockedRun->id,
'seq' => $nextSequence,
'event_type' => $result->eventType,
'stage_id' => $result->stageId,
'occurred_at' => now(),
'payload' => $result->safePayload,
'visibility' => $result->visibility,
'idempotency_key' => $data['command_id'],
'correlation_id' => $result->correlationId,
'causation_id' => $data['command_id'],
]);
$lockedRun->forceFill(['current_seq' => $nextSequence])->save();
DB::afterCommit(
static fn () => broadcast(
new SimulationEventCommitted($event->id)
)
);
return $event;
}, attempts: 3);
return response()->json([
'accepted' => true,
'event_id' => $event->id,
'sequence' => $event->seq,
], 202);
}
}
In production, the event and outbox record should preferably be written in the same transaction, with an independent worker handling publication. Broadcasting directly after commit is sufficient for a prototype, but an outbox is more resilient when the realtime broker is temporarily unavailable.
JavaScript VR-rendering example
This compact three.js example fetches a snapshot, renders stage nodes, enables WebXR, applies ordered realtime events, and submits a constrained command when the user selects a stage. Production code should add labels, recovery logic, accessibility controls, error telemetry, resource disposal, and explicit asset budgets.
import * as THREE from 'three';
import { VRButton } from 'three/addons/webxr/VRButton.js';
const runId = document.body.dataset.runId;
const apiBase = '/api/v1';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x10131a);
const camera = new THREE.PerspectiveCamera(
65,
window.innerWidth / window.innerHeight,
0.1,
100
);
camera.position.set(0, 1.6, 4);
const renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: 'high-performance'
});
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.xr.enabled = true;
document.body.append(renderer.domElement, VRButton.createButton(renderer));
scene.add(new THREE.HemisphereLight(0xffffff, 0x303040, 2));
const stageMeshes = new Map();
let currentSequence = 0;
function setStageState(mesh, state) {
const active = state === 'active';
const completed = state === 'completed';
mesh.scale.setScalar(active ? 1.25 : 1);
mesh.material.emissive.setHex(active ? 0x3355aa : 0x000000);
mesh.material.opacity = completed ? 0.55 : 1;
mesh.userData.state = state;
}
function renderSnapshot(snapshot) {
currentSequence = snapshot.through_seq;
for (const [index, stage] of snapshot.stages.entries()) {
const geometry = new THREE.BoxGeometry(0.8, 0.8, 0.8);
const material = new THREE.MeshStandardMaterial({
color: 0x4d83b8,
transparent: true
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set((index - snapshot.stages.length / 2) * 1.25, 1.4, 0);
mesh.userData.stageCode = stage.code;
setStageState(mesh, stage.state);
stageMeshes.set(stage.code, mesh);
scene.add(mesh);
}
}
function applyEvent(event) {
if (event.sequence !== currentSequence + 1) {
recoverEvents(currentSequence).catch(console.error);
return;
}
currentSequence = event.sequence;
if (event.event_type === 'stage.transitioned') {
const previous = stageMeshes.get(event.payload.from_stage);
const next = stageMeshes.get(event.payload.to_stage);
if (previous) setStageState(previous, 'completed');
if (next) setStageState(next, 'active');
}
if (event.event_type === 'indicator.observed') {
const target = stageMeshes.get(event.stage.code);
if (target) {
target.rotation.y += Math.PI / 4;
}
}
}
async function recoverEvents(afterSequence) {
const response = await fetch(
`${apiBase}/runs/${runId}/events?after_seq=${afterSequence}`,
{ credentials: 'include' }
);
if (!response.ok) throw new Error('Unable to recover run events');
const body = await response.json();
for (const event of body.events) applyEvent(event);
}
async function submitCommand(type, payload = {}) {
const response = await fetch(`${apiBase}/runs/${runId}/commands`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({
command_id: crypto.randomUUID(),
type,
expected_sequence: currentSequence,
payload
})
});
if (response.status === 409) {
await recoverEvents(currentSequence);
return;
}
if (!response.ok) throw new Error('Command rejected');
}
const raycaster = new THREE.Raycaster();
const tempMatrix = new THREE.Matrix4();
const controller = renderer.xr.getController(0);
controller.addEventListener('select', () => {
tempMatrix.identity().extractRotation(controller.matrixWorld);
raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
const hits = raycaster.intersectObjects([...stageMeshes.values()]);
const selected = hits[0]?.object;
if (selected) {
submitCommand('inspect_indicator', {
stage_code: selected.userData.stageCode
}).catch(console.error);
}
});
scene.add(controller);
const snapshotResponse = await fetch(
`${apiBase}/runs/${runId}/snapshot`,
{ credentials: 'include' }
);
if (!snapshotResponse.ok) throw new Error('Unable to load run');
renderSnapshot(await snapshotResponse.json());
const socket = new WebSocket(
`wss://${location.host}/ws/runs/${encodeURIComponent(runId)}`
);
socket.addEventListener('message', ({ data }) => {
const message = JSON.parse(data);
if (message.type === 'simulation.event') applyEvent(message.event);
});
renderer.setAnimationLoop(() => renderer.render(scene, camera));
WebGL 2 provides the underlying browser rendering context used by engines such as three.js and Babylon.js, while WebXR supplies immersive display and input integration. Raw WebGL and raw WebXR are standards layers; most projects should use an engine unless highly specialized rendering requirements justify writing substantial infrastructure.
Framework and version recommendations
Versions below are a July 31, 2026 baseline, not permanent pins. Production builds should use the latest compatible security patch, commit Composer and npm lockfiles, test upgrades in CI, and record dependencies in a software bill of materials.
Rendering and XR frameworks
| Framework | Recommended version line | Advantages | Disadvantages | Best fit |
|---|---|---|---|---|
| three.js | r185 | Flexible scene graph, broad examples and loader ecosystem, direct WebXR integration, strong control over instancing, custom shaders, picking, and optimization | Requires more application architecture, GUI, collision, accessibility, and tooling work than a full engine | Preferred for a custom instructional visualization with a capable JavaScript team |
| Babylon.js | 9.19.x | More integrated game-engine experience, inspector, GUI, asset management, physics options, WebXR helpers, scene optimization facilities | Larger and more opinionated abstraction surface; engine conventions can be excessive for a modest visualization | Strongest alternative when the team wants an integrated engine rather than a renderer toolkit |
| A-Frame | 1.8.x | Declarative HTML-like authoring, low entry barrier, rapid WebXR prototype development, component model | Less direct control for complex data-driven scenes; abstraction and underlying three.js version can lag; large applications may need substantial custom components | Best for proof-of-concept, museum-style guided tours, or a small team new to 3D |
| Raw WebXR plus WebGL 2 | Current specifications | Maximum control and few framework assumptions | Requires extensive work for scene graphs, loaders, materials, input abstractions, interaction, optimization, and compatibility | Only for research prototypes or highly specialized engines |
| WebGPU path | Feature-detected, optional | Future-facing rendering and compute capabilities | XR and browser/device support must be verified; using it as the only backend would reduce reach | Treat as an optional later optimization, not the MVP compatibility baseline |
three.js r185 was released in July 2026. Babylon.js 9.19.0 was released on July 30, 2026. A-Frame 1.8.0 was released in June 2026, updated its underlying three.js dependency to r184, and included controller and strict-CSP fixes.
Recommendation: choose three.js unless rapid declarative prototyping is more important than long-term control. Choose Babylon.js when its integrated GUI, inspector, engine utilities, and WebXR feature set replace enough custom work to justify its larger abstraction. Use A-Frame for a time-boxed prototype or simple guided exhibit, but conduct an architectural review before scaling it into a complex authoring and multiuser platform.
Application-shell frameworks
| Framework | Recommended line | Advantages | Disadvantages | Recommendation |
|---|---|---|---|---|
| React | 19.2, latest patched release | Large ecosystem, mature state libraries, good fit for dashboards and authoring tools | Careless component rerendering can interfere with scene ownership; avoid mapping every 3D object to a DOM component | Suitable default, with the 3D canvas managed imperatively and React Server Components excluded from the client-only XR surface |
| Vue | Vue 3, latest stable patch, built with Vite | Lower ceremony, approachable reactivity, effective for compact UI shells | Smaller enterprise ecosystem than React or Angular in some organizations | Excellent for a small team and a clean separation between DOM UI and 3D scene |
| Angular | 22.1 line | Strong conventions, dependency injection, routing, forms, testing, and RxJS-based event handling | Highest framework weight and learning overhead; lifecycle integration with a render loop needs discipline | Appropriate where Angular is already an organizational standard |
| No large UI framework | Native modules, web components, small state store | Minimal runtime and direct control | Authoring and administration screens become harder to maintain | Reasonable for the first single-scene technical prototype |
React’s documented current line was 19.2, while Angular 22.1 was current in late July 2026. React has also published security fixes affecting some server-component configurations, reinforcing the need to remain on patched releases and to avoid unnecessary server-component exposure in a client-rendered XR application. Vue CLI is in maintenance mode; new Vue projects should use Vite-based tooling.
Do not let the UI framework own high-frequency scene state. Maintain the 3D scene through an engine adapter, update it from semantic state changes, and leave the engine’s render loop outside React, Vue, or Angular change-detection cycles.
PHP and realtime choices
| Technology | Recommended line | Advantages | Disadvantages | Position |
|---|---|---|---|---|
| PHP | 8.5.x, latest security patch | Current language improvements and supported lifecycle | Requires dependency compatibility testing, especially for older enterprise packages | Default runtime |
| Laravel | 13.x | Fast delivery, expressive validation and policies, queues, cache, rate limiting, broadcasting, Reverb integration | Annual major releases and convention-heavy design; long-running worker discipline is required with Octane/Reverb | Preferred for MVP and product teams prioritizing speed |
| Symfony | 7.4 LTS | Modular components, explicit architecture, long security-support horizon | More initial assembly and configuration | Preferred for conservative institutional or enterprise deployment |
| Symfony | 8.1.x | Latest framework features | Shorter maintenance horizon than the LTS branch | Use when the team intentionally follows frequent upgrades |
| Laravel Reverb | Version compatible with Laravel 13 | First-party Laravel broadcasting and WebSocket integration | Operating persistent sockets still demands capacity planning and monitoring | Default realtime option for an all-PHP Laravel stack |
| Ratchet or OpenSwoole | Latest supported compatible versions | PHP-native persistent WebSocket/event-loop alternatives | Smaller operational talent pool and more custom integration than standard request/response PHP | Consider for specialist PHP teams |
| Node.js bridge | Node 24 LTS | Mature realtime and media ecosystem; useful for signaling, protocol translation, or isolated socket workloads | Adds another runtime, deployment path, dependency tree, and observability surface | Add only when it clearly reduces complexity elsewhere |
PHP 8.5 was the current supported release line, with PHP 8.5.9 published on July 30, 2026. Symfony listed 8.1 as stable and 7.4 as its LTS branch, with 7.4 security support extending to November 2029. Node’s own guidance says production applications should use Active or Maintenance LTS; Node 24 was an LTS line in July 2026, while Node 26 remained Current.
Recommended deployment profiles:
| Profile | Stack |
|---|---|
| Fastest safe MVP | PHP 8.5, Laravel 13, PostgreSQL, REST, SSE initially, A-Frame 1.8 or three.js r185, Vite |
| Recommended product architecture | PHP 8.5, Laravel 13 or Symfony 7.4 LTS, PostgreSQL, Redis, transactional outbox, WebSocket, three.js r185, TypeScript, React or Vue |
| Enterprise institutional deployment | PHP 8.5, Symfony 7.4 LTS, organization OIDC provider, PostgreSQL, Redis, managed object storage/CDN, separate WebSocket tier, centralized OpenTelemetry |
| Media-rich multiuser extension | Product architecture plus Node 24 LTS WebRTC signaling/media bridge; authoritative simulation state remains in PHP-backed services |
UX, performance, security, and validation
Instructional and interaction design
Immersion should make causality spatially understandable rather than merely decorative. CAMIL identifies presence and agency as important affordances of immersive learning, but also observes that highly engaging environments can increase cognitive load and weaken self-regulation unless learners receive structure and reflection opportunities.
| Mode | Interaction pattern | Teaching purpose | Design requirement |
|---|---|---|---|
| Guided tour | Narrated progression, highlighted evidence, constrained choices, pause-and-explain moments | Novice orientation and conceptual understanding | One instructional focus at a time; optional captions; explicit transition summaries |
| Branching scenario | Learner selects defensive controls and sees consequences | Causal reasoning and decision practice | Explain why an action succeeded, failed, or arrived too late |
| Analysis mode | Freeze, rewind, filter, compare ground truth with observable evidence | Incident reconstruction and ATT&CK mapping | Preserve sequence and provenance; show uncertainty rather than omniscient conclusions |
| Sandbox | Learner rearranges controls, changes visibility, and reruns safe simulations | Systems thinking and experimentation | Bound all inputs to a declarative scenario model; no arbitrary scripting |
| Facilitated multiuser exercise | Participants occupy analyst, incident commander, identity, network, or endpoint roles | Team communication and coordination | Shared authoritative state, role-specific visibility, facilitator pause/rollback, moderated communication |
| Desktop companion | Keyboard, mouse, touch, and accessible two-dimensional views | Access and broader deployment | Functional parity for all learning-critical operations |
Useful spatial patterns include:
- A stage corridor or radial timeline for the high-level Lockheed sequence.
- A network-and-identity graph showing assets, trust zones, lateral paths, and blast radius.
- An evidence lens that reveals only the logs and indicators available to a defender at that moment.
- A time scrubber for pause, rewind, replay, and counterfactual comparison.
- Intervention portals where the learner chooses controls and predicts consequences before seeing the outcome.
- A ground-truth toggle available only after the decision, preventing hindsight from contaminating assessment.
- Debrief boards that map decisions to ATT&CK techniques, mitigations, observations, and missed opportunities.
- Teleport locomotion, snap turning, seated mode, adjustable height, captions, nonspatial audio alternatives, reduced-motion mode, and desktop parity.
A 2026 cybersecurity-education study reported stronger retention and spatial presence for its immersive VR condition than its textbook, video, and desktop-VR comparisons, while also presenting desktop VR as a more accessible alternative. This is promising evidence, but it should not be generalized to every design; the simulation still requires controlled evaluation against a non-VR version.
Performance and scalability
WebXR applications are latency-sensitive because the browser must continuously produce views corresponding to tracked poses. Stable frame pacing is more important than visual richness. Quality should degrade predictably before frame rate becomes uncomfortable.
Recommended rendering budgets and techniques include:
- Use glTF/GLB as the delivery format, KTX2-compressed textures, and optionally Draco or Meshopt geometry compression. Draco reduces transfer size but adds client decoding work, so profile total load time rather than optimizing file size alone.
- Use instancing for repeated endpoints, packets, indicators, and topology markers. three.js documents
InstancedMeshas a way to reduce draw calls for repeated geometry. - Apply level of detail, frustum culling, distance-based label suppression, pooled effects, light baking, and limits on real-time shadows.
- Split scenarios into stage- or zone-specific asset bundles and preload the next likely branch.
- Parse large event batches and calculate graph layouts in Web Workers.
- Keep React, Vue, or Angular updates away from the per-frame rendering path.
- Reduce pixel ratio, shadow quality, particles, postprocessing, and annotation density dynamically when measured frame time exceeds the device budget.
- Dispose of geometries, materials, textures, audio buffers, event listeners, and XR input objects when changing scenarios.
- Use engine-level tools such as Babylon’s scene optimizer when choosing Babylon.js.
On the server side:
- Keep ordinary PHP API instances stateless and horizontally scalable.
- Store sessions, rate-limit counters, presence, and fan-out metadata outside individual application instances.
- Partition or index events by run and sequence; archive completed-run telemetry separately from active events.
- Use snapshots so a reconnect does not replay an entire long exercise.
- Apply backpressure to telemetry and realtime publishers.
- Broadcast semantic deltas rather than complete state documents.
- Isolate high-volume WebSocket workloads if persistent connections begin to compete with API capacity.
- Do not introduce WebRTC peer meshes for general state synchronization; they become inefficient and difficult to govern as group size grows.
Application and content security
The OWASP API Security Top 10 highlights risks directly relevant to this product, including broken object-level authorization, broken authentication, unrestricted resource consumption, broken function-level authorization, server-side request forgery, and unsafe consumption of APIs. Every scenario, run, event stream, metric, and export therefore needs object-level checks rather than route-level authentication alone.
Core controls should include:
| Control area | Required implementation |
|---|---|
| Identity | OIDC or another established organizational identity mechanism; phishing-resistant MFA for authors and administrators where available |
| Browser session | Secure, HttpOnly, SameSite cookies when using cookie authentication; CSRF defense for state-changing requests; short-lived realtime tickets |
| Authorization | Run membership plus role and resource checks on every API call, WebSocket subscription, event recovery request, metric query, and asset manifest |
| Input validation | Versioned JSON Schema or equivalent server validation; reject unknown command types, excessive nesting, unbounded arrays, arbitrary URLs, markup, and executable strings |
| Output safety | Contextual output encoding; sanitized learner text; no direct innerHTML; no interpretation of scenario fields as code |
| Content Security Policy | Restrict scripts, workers, connections, images, audio, and models to approved origins; avoid unsafe-eval; use nonces or hashes where appropriate |
| Asset safety | Quarantine uploads; validate file type, size, dimensions, mesh counts, texture budgets, and supported glTF extensions; transcode assets before publication |
| Realtime security | Validate Origin; authenticate upgrades; authorize channels; limit connection count, message rate, and frame size; terminate idle or malformed clients |
| Rate limiting | Separate quotas for login, run creation, commands, event recovery, telemetry, exports, and asset uploads |
| Auditability | Immutable records for publication, role changes, exports, facilitator overrides, and administrative access |
| Dependency security | Composer and npm lockfiles, automated advisory checks, SBOM generation, signed deployment artifacts, prompt patching |
| Secrets | Managed secret storage; no credentials in JavaScript bundles, scenarios, 3D files, or container images |
| Privacy | Collect only metrics tied to explicit learning or operational questions; aggregate or discard high-frequency pose information client-side |
Telemetry privacy
Do not persist raw headset pose, controller pose, hand-joint, gaze, microphone, or room-mapping data by default. Derive limited measures such as:
time spent at stage
number of evidence objects inspected
decision latency
incorrect interventions
help requests
navigation discomfort report
frame-time percentile
disconnect and recovery count
Use pseudonymous participant identifiers in analytics, keep account mapping in a more restricted system, and set a deletion date when data is written. Cohort dashboards should suppress small groups and avoid ranking individuals where the educational purpose does not require it.
Testing and evaluation
A rigorous validation plan needs four complementary levels.
Software and domain testing. Unit-test transition rules, scoring, visibility, ATT&CK-reference resolution, command allowlists, and privacy filters. Use property-based tests to generate unexpected event sequences and verify invariants such as monotonic sequence numbers, no transition after completion, and no learner access to facilitator-only evidence.
Integration and system testing. Test database transactions, outbox delivery, duplicated commands, reconnection, delayed messages, schema upgrades, snapshot rebuilding, browser refresh, and headset session interruption. Use contract tests for OpenAPI, GraphQL, event schemas, and realtime messages.
Rendering and device testing. Maintain a matrix of representative standalone headsets, desktop browsers, controllers, hand tracking where supported, and non-XR fallback. Test low-memory conditions, context loss, slow asset delivery, WebSocket interruption, controller disconnect, repeated XR entry/exit, seated mode, text legibility, and motion comfort. Official WebXR samples are useful as minimal capability references and device-diagnostic baselines.
Security testing. Include static analysis, dependency scanning, secret scanning, dynamic API tests, authorization matrix tests, WebSocket fuzzing, upload validation tests, CSP verification, SSRF probes, rate-limit tests, and a review of scenario content for operational misuse. A penetration test should explicitly attempt cross-run access, facilitator impersonation, event forgery, sequence replay, malicious glTF uploads, telemetry exfiltration, and denial of service.
Learning evaluation should use both immediate and delayed measures:
| Dimension | Example measure |
|---|---|
| Factual knowledge | Correct identification of stages, tactics, indicators, and controls |
| Causal understanding | Explanation of how one event enabled the next and where intervention was possible |
| Transfer | Performance on a structurally similar scenario with different surface details |
| Decision quality | Correct intervention, timing, prioritization, and confidence calibration |
| Retention | Repeated assessment after an appropriate delay |
| Usability | System Usability Scale or another established instrument |
| Workload | NASA-TLX or a suitable workload instrument |
| Presence and agency | Validated presence or embodiment scales relevant to the implementation |
| Comfort | Simulator-sickness symptoms, early exit, locomotion changes, and help requests |
| System quality | Frame-time percentiles, dropped frames, long tasks, event lag, reconnects, memory, and load time |
The preferred study is a randomized or counterbalanced comparison of VR and desktop versions using identical learning content. Use a pretest, immediate post-test, delayed test, scenario-transfer task, usability/workload measures, and qualitative interviews. Define primary outcomes before analysis and use power analysis rather than choosing sample size solely from available participants. Because instructional method interacts with immersive media, include reflection or self-explanation prompts as a tested design factor rather than evaluating headset use alone.
Roadmap, minimal viable prototype, and reference implementations
The estimates below are planning assumptions for a team already competent in PHP and modern JavaScript but not necessarily in WebXR. One person-week means one full-time engineer-week. “Low” is up to one person-week, “Medium” is approximately two to three, and “High” is four or more. Custom art production and institutional procurement are excluded.
Implementation roadmap
| Milestone and task | Effort | Indicative effort | Exit criterion |
|---|---|---|---|
| Governance and ethical-use policy | Medium | 1–2 person-weeks across legal/security/SME roles | Written content boundary, authorization policy, telemetry inventory, scenario-review process |
| Canonical model and learning objectives | Medium | 2 person-weeks | Approved Lockheed/ATT&CK crosswalk, assessment rubric, first synthetic scenario outline |
| Domain schema and API contracts | Medium | 2–3 person-weeks | Versioned relational schema, JSON event schemas, OpenAPI contract, authorization matrix |
| PHP foundation | Medium | 2–3 person-weeks | Authentication, scenario retrieval, run creation, command endpoint, event store, migrations |
| Desktop 3D scene | Medium | 3–4 person-weeks | Seven-stage scene, asset loading, evidence interactions, state reducer, desktop navigation |
| Single-user WebXR | High | 3–5 person-weeks | Headset entry, controller selection, teleport/snap turn, seated mode, acceptable device performance |
| Guided scenario and debrief | Medium | 2–3 person-weeks | One end-to-end scenario, interventions, feedback, rewind, ATT&CK debrief |
| Realtime synchronization | High | 3–5 person-weeks | WebSocket or SSE delivery, sequence recovery, snapshots, reconnect behavior |
| Security and privacy hardening | High | 3–5 person-weeks | Content validation, object authorization tests, CSP, rate limits, telemetry minimization, audit trail |
| Automated and device testing | High | 4–6 person-weeks | CI test suite, browser/headset matrix, load tests, recovery tests, security test report |
| Instructor dashboard | Medium | 2–3 person-weeks | Start/pause/reset run, participant status, event timeline, approved metrics |
| Multiuser and presence | High | 4–7 person-weeks | Role-specific clients, authoritative shared state, bounded pose updates, facilitator controls |
| Formal pilot evaluation | High | 4–8 person-weeks | Approved study protocol, pilot dataset, qualitative findings, prioritized revisions |
| Production operations | High | 4–8 person-weeks | Infrastructure as code, monitoring, backup/recovery, retention automation, deployment runbooks |
Activities overlap. A tightly scoped MVP does not require the full sum of the table; a production pilot does.
Minimal viable prototype
A defensible MVP should include:
| Area | MVP scope |
|---|---|
| Scenario | One fictional phishing-to-impact storyline; seven narrative stages; approximately 12–20 visible entities |
| Models | Lockheed stage labels plus selected ATT&CK tactics and techniques pinned to a specific ATT&CK release |
| Modes | Guided tour and constrained branching exercise |
| Platforms | Desktop browser plus one primary WebXR headset family; desktop remains functionally complete |
| Interactions | Inspect evidence, answer a prompt, apply one of several predefined defensive controls, advance after feedback |
| State | Server-authoritative run, append-only events, sequence recovery, deterministic replay |
| Transport | REST plus SSE or a basic WebSocket channel |
| PHP | Authentication, scenario API, run API, command validation, event persistence, telemetry batching |
| JavaScript | 3D stage view, WebXR controller selection, teleport/snap turn, event reducer, simple debrief |
| Metrics | Completion, decision accuracy, decision latency, evidence inspected, help use, performance diagnostics |
| Security | No uploads, no custom scripts, no external URLs, synthetic indicators only, object-level authorization, rate limits |
| Exclusions | Multiuser avatars, WebRTC voice, AI-generated scenarios, real SIEM integration, cyber-range execution, gaze recording, comprehensive authoring |
Suggested acceptance criteria are:
- The same scenario can be completed in desktop and immersive modes without losing learning-critical functionality.
- Refreshing or reconnecting reconstructs the exact run state.
- Replaying the same event stream yields the same scene and score.
- No scenario field can cause code execution, arbitrary network access, or loading from an unapproved origin.
- A learner cannot access another run, facilitator-only evidence, or cohort telemetry.
- The application maintains the selected headset’s required frame cadence under the defined scene budget.
- At least one formative pilot demonstrates that learners can explain the causal chain, not merely recall stage names.
- A privacy review confirms that no raw pose, gaze, voice, or room data is retained by default.
The MVP is approximately 12–16 person-weeks. A production classroom pilot with instructor tooling, hardened realtime synchronization, broader device testing, formal content review, and evaluation is more plausibly 30–45 person-weeks, excluding bespoke 3D art and enterprise procurement.
Primary references and open-source examples
| Reference or project | Type | What to reuse | Important limitation |
|---|---|---|---|
| Hutchins, Cloppert, and Amin, Intelligence-Driven Computer Network Defense… | Original industry paper | Seven-phase narrative, campaign analysis, disruption-oriented teaching | Linear abstraction should not be presented as a complete model of modern adversary behavior. |
| MITRE ATT&CK Design and Philosophy | Primary methodology paper | Tactic/technique semantics, empirical grounding, coverage cautions, defensive and emulation use cases | The paper is a methodology source; current object content must come from the versioned ATT&CK data. |
| ATT&CK STIX/TAXII data | Primary machine-readable source | Automated imports, identifiers, descriptions, relationships, version pinning | Transform into a curated educational subset; do not expose the entire corpus to learners. |
| ATT&CK Navigator | Open-source visualization | Layer documents, filtering, highlighting, matrix interaction, mapping displays | It is a two-dimensional matrix tool, not a simulation runtime. |
| ATT&CK Workbench | Open-source content tooling | Editing workflows, ATT&CK object management, local knowledge-base patterns | Its data-authoring model still needs instructional and spatial extensions. |
| MITRE CALDERA | Open-source adversary-emulation platform | Terminology, operation timelines, adversary-profile concepts, plugin architecture ideas | Do not embed executable abilities in a public educational simulator; integration requires a separately authorized range. |
| W3C WebXR Device API and Immersive Web samples | Primary standard and reference demos | Session lifecycle, input, reference spaces, capability checks, minimal diagnostic examples | Samples are deliberately small and do not supply application architecture, accessibility, or security policy. |
| three.js examples and loaders | Open-source rendering examples | WebXR setup, controller interaction, glTF, KTX2, Draco, instancing, picking | Examples require production error handling, disposal, accessibility, and state architecture. |
| Khronos glTF and KTX specifications | Primary format standards | Runtime asset format, texture packaging, compression pipeline | Asset safety, extension allowlists, and complexity budgets remain application responsibilities. |
| OASIS STIX 2.1 | Primary CTI standard | Interchange for indicators, threat actors, attack patterns, relationships, and bundles | Not a substitute for run events, spatial entities, learning objectives, or learner telemetry. |
| Makransky and Petersen’s CAMIL | Original academic model | Design hypotheses concerning presence, agency, cognitive load, reflection, and learning | It motivates evaluation; it does not guarantee that an immersive implementation will outperform desktop instruction. |
| Rehman et al., immersive VR cybersecurity education study | Academic empirical study | Evaluation dimensions, desktop/immersive comparison, engagement, retention, usability, presence | Results are study- and implementation-specific and should be replicated for the proposed kill-chain curriculum. |
The central design decision is to make semantic events—not visual effects—the core product. A carefully versioned scenario graph and authoritative event stream can support desktop, VR, replay, assessment, instructor dashboards, analytics, and future engines. A scene built first without that domain model is likely to become an attractive but brittle demonstration. Conversely, a secure event model with strong instructional design can begin as a modest desktop/VR prototype and evolve into a validated simulation platform without crossing the boundary into operational offensive tooling.