cardboard
elonn-world-cardboard is the Elonn runtime for Google Cardboard headsets. It
runs on an Android phone placed inside a Cardboard viewer and renders the Elonn
field and carry layers in stereoscopic VR using the phone gyroscope and
magnetometer for head tracking.
Cardboard implements the World contract independently following the Runtime Contract, as every Elonn runtime does. Its presentation and interaction layers are VR-specific and separate from the Android AR runtime.
The Runtime Contract is the normative runtime contract. This page describes the current Cardboard implementation and platform constraints.
Local setup
- Package:
com.elonn.worldcardboard - Runtime ID:
cardboard(sent inX-Elonn-Runtime) - Runtime version:
0.1.0 - VR SDK:
com.google.cardboard:cardboard-xr(Maven) - Min SDK: API 24
- Required permissions:
CAMERA(passthrough),ACCESS_FINE_LOCATION(field),BLUETOOTH_CONNECTAPI 31+ (peripheral detection) - No ARCore dependency.
- World base URL and login URL must be configurable without code changes. Store in
local.properties:
WORLD_BASE_URL=https://world.elonn.com
LOGIN_URL=https://elonn.com/account/login
What it renders
Field layer — Live CameraX feed split into left/right eye viewports,
forming a stereoscopic passthrough view of the real world. Field objects from
field_runtime.objects[] are rendered as markers overlaid on each eye's
viewport at the correct bearing. Objects with presentation.marker=false are
not rendered as markers. Required provider attribution from
field_runtime.objects[].attribution is displayed without provider-specific
branching. The Runtime Contract heading-relative projection formula applies directly:
relativeBearing = (bearing − deviceHeading + 360) % 360, folded to [−180, 180]
cull if |relativeBearing| > hFOV / 2
projectedX = (relativeBearing / hFOV + 0.5) × viewportWidth
deviceHeading comes from the gyroscope/magnetometer. viewportWidth is
half the physical screen width (one eye). The renderer derives a separate
per-eye projection shift from the Cardboard viewer profile.
Carry layer — Cardboard currently renders compact head-locked shell
surfaces over the passthrough view: center stack, left rail, right rail, and
lower dock. This is a partial carry implementation. Full conformance requires
preserving every supported World-supplied stack, including finding_overlay,
deriving focus from each stack's focused_surface_id, and keeping transient
panel payloads separate from durable surface identity.
Runtime v2 dataset — Cardboard parses much of the World runtime dataset:
top-level service, contract, runtime session, capabilities, status, shell dock,
field runtime, surface runtime, commands, viewports, service descriptors,
semantic action ids/labels, object presentation, object actions, and
attribution. It is not fully conforming until it consumes the shared contract
fixtures as first-class tests and preserves the authoritative
surface_runtime shape without local filtering.
Input model
Cardboard paired with a Bluetooth peripheral is an Android computer with a head-tracked display. Input is not constrained to the single Cardboard trigger.
Head — Primary aiming device. The heading vector is the cursor. Looking at a field marker or carry surface selects the target; a peripheral input confirms.
Bluetooth mouse — Implemented as a runtime pointer. Android's platform
cursor is hidden and Cardboard draws a small stereo GL pointer. Mouse motion
enables pointer_input and refreshes the World session once. Left click
dispatches dock, panel navigation, or Field control actions. Right click
returns to carry. Scroll wheel flips the focused surface in carry_primary.
Bluetooth keyboard — Planned. Text input for Find queries and panel forms. Tab = next surface. Esc = back / close. Space or Enter = select.
Bluetooth controller — Planned. Left stick = navigate. Right stick or d-pad = rotate cursor. A = select. B = back. X = action. Y = field controls.
Head-gaze fallback — Planned. Without a peripheral, look at an object for one second to select. Used for pure Cardboard with only the magnetic trigger.
The Runtime Contract action model maps cleanly to all input modes — a mouse click, controller trigger, screen tap, hand pinch, or gaze confirmation are all the same confirm action. The input device changes; the contract does not.
Declared capabilities
Cardboard sends this capability declaration in X-Elonn-Runtime on every
session request:
{
"runtime": "cardboard",
"version": "0.1.0",
"capabilities": {
"stereo_rendering": true,
"headset_mode": true,
"camera_background": true,
"field_markers": true
}
}
camera_background: true is always declared — passthrough is not optional.
pointer_input: true is added dynamically after a Bluetooth mouse or equivalent
pointing device is detected or used.
Runtime data path
Identical to Android. Cardboard talks only to World:
GET /world/identity/me— authenticated member identity (bearer token)GET /world/session— full runtime contract with field objects, surfaces, stacks, commandsGET /world/panels/{service}— panel payloads for focused surfacesPOST /world/find/query— Find queries; returns updatedsurface_runtimePOST /world/surface/surfaces/{surface_id}/focus— focus writesPOST /world/surface/surfaces/{surface_id}/placement— placement writes
Cardboard does not call API, Social, Messages, Maps, Time, Surface, or Find directly.
Partial World responses do not remove recovery UI. Cardboard preserves the existing shell dock, services, commands, and canonical carry/context stacks when a response omits them, so the lower dock remains available after Find, Field, or focused panel updates.
Relation to Android
Cardboard is a standalone app, not a mode or fork of the Android runtime. Both implement the same World contract independently from the Runtime Contract — that is the architecture. The Android runtime's World client is a reference for how the the runtime contract was applied on a similar platform, not a library Cardboard inherits.
The capability declaration is a per-session contract: a runtime declares what it is on load, and World tailors the response to those capabilities. An app that switches between AR and VR mid-session would need to re-fetch the session on mode change, which is not how the model is designed. One runtime, one capability set, one session.