Omni SpatialQR
Zero-Friction WebAR Framework. A blazing-fast Vanilla JS library for mapping rich HTML interfaces and 3D models perfectly onto physical QR codes.
The physical world is static. The digital world is limitless. Omni SpatialQR is the bridge between them.
Until now, Augmented Reality required massive friction: forcing users to download heavy apps, scanning weird proprietary markers, or waiting for bloated 3D engines to load. Omni SpatialQR changes the paradigm. It turns standard, everyday QR codes—on business cards, restaurant menus, retail packaging, and real estate signs—into instant, zero-friction spatial interfaces directly in the mobile browser.
Make your print media play video. Turn a coaster into a 3D showroom. Turn a poster into a glassmorphic checkout interface. It’s lightweight, instantly familiar to users, and feels like native magic.
README.md
A vanilla JavaScript library that calculates camera pose homography from the native BarcodeDetector API to project HTML DOM elements and Three.js WebGL models onto physical QR codes via mathematically precise CSS matrix3d transforms.
Installation
<script src="omni-spatial-qr.js"></script>
Minimal Quick Start
const visionAR = new OmniSpatialQR({
container: '#ar-wrapper',
type: 'dynamic'
});
visionAR.start();
// Essential for SPA (React/Vue) routing to prevent memory leaks
visionAR.stop();
Use Cases & Data Routing
Physical QR codes should be as simple as possible (low data density) so they scan instantly from far away. Omni SpatialQR provides two powerful architectures to keep your codes microscopic while rendering massive data payloads.
Use Case A: The Universal Billboard
Use this if your QR codes are standard URLs designed to be scanned by native iPhone/Android cameras to open your website. The library automatically extracts your query parameters (e.g. https://mysite.com?promo=vip).
const visionAR = new OmniSpatialQR({
type: 'dynamic',
parseParam: ['promo'], // Safely extracts { promo: "vip" }
assetResolver: async (parsedObj) => {
if (parsedObj.promo === 'vip') {
// Return JSON to instantly build a Glassmorphic Card!
return {
title: "VIP Status",
description: "Welcome back.",
buttons: [{ label: "Enter", color: "#22c55e" }]
};
}
}
});
Use Case B: The Lightning In-App Scanner
If users scan codes inside your webapp, you don't need full URLs. Use custom URIs (e.g., omni:vip). This makes the physical QR code smaller, allowing the camera to track it significantly faster and from much further away.
const visionAR = new OmniSpatialQR({
type: 'dynamic',
uriPrefix: 'omni', // Intercepts "omni:vip"
assetResolver: async (payload) => {
// Payload is the raw string "vip"
const dbItem = await fetch(`/api/items/${payload}`);
return dbItem.json();
}
});
Use Case C: The 3D Model Viewer
Ideal for e-commerce or museums. Map physical items to their 3D digital twins automatically.
const visionAR = new OmniSpatialQR({
type: 'dynamic',
enable3D: true, // Triggers Three.js CDN download
autoRotate3D: true, // Creates a turntable effect
uriPrefix: 'model',
assetResolver: async (payload) => {
if (payload === 'shoe') return 'https://mysite.com/shoe.glb';
}
});
Type Overrides & Custom Injection
Direct Type Forcing
You can bypass the assetResolver completely and explicitly force a UI type directly within the QR Code string:
- URL Parameter: Append
?omni=type. Example: https://mysite.com/file?omni=audio guarantees the Audio Pill UI builds.
- Omni URI Scheme: Prefix the string with
type:payload. Example: video:https://mysite.com/vid.mp4 or 3d:duck.glb renders the specific player/model.
AssetResolver Overrides & Error Handling
If your backend returns a video URL without an extension (e.g. https://api.com/file_77), dynamic mode will fail. Your resolver can return a config object to force the engine.
Furthermore, if your database lookup fails, return null. The engine will gracefully abort the visual update and fire the onError hook.
assetResolver: async (id) => {
if (!isValid(id)) return null; // Aborts render, fires onError()
return {
type: 'video', // Forces the Video Player UI
data: 'https://api.com/file_77'
};
}
Custom HTML vs. DOM Templates
You have two distinct ways to render custom user interfaces in 3D:
1. DOM Templates (Static): Best for React/Vue components already mounted on your page. Map an ID string to a hidden DOM node.
<!-- Your hidden custom UI -->
<div id="my-rating" style="display:none;">⭐⭐⭐⭐⭐</div>
const visionAR = new OmniSpatialQR({
templates: {
'stars': '#my-rating'
}
});
// If the scanner reads the exact string "stars", it clones your HTML!
2. Raw HTML Injection (Dynamic): Best for backend-driven content. Simply return an object with an html property from your resolver.
assetResolver: async (payload) => {
return { html: "<div class='live-price'>$99.00</div>" };
}
CSS Resolution Nuance: The library uses a strict CSS reset (all: initial) to protect your layout, but explicitly inherits your font-family. Due to the default overlayResolution: 100, 1 unit equals 1 physical pixel relative to the QR code size. Scale your CSS padding and fonts accordingly!
Full Configuration API Reference
- container (String): DOM selector for the AR viewport.
- type (String): 'dynamic', 'square', 'text', 'card', 'image', 'video', 'audio', '3d', 'youtube', 'customHtml'
- scale (Number): Base scale multiplier (Default 1.0).
- position (String): 'center', 'top', 'bottom', 'left', 'right'.
- gap (Number): Unit offset if position is not center.
- overlayResolution (Number): Base render matrix (Default 100). Higher numbers increase clarity but can reduce performance. Use 100 for optimal performance.
- spawnAnimation (Boolean): Triggers a premium Apple-style pop-up animation for all DOM overlays and 3D models when they spawn.
- autoPlay (Boolean): Auto-plays injected media loops.
- showControls (Boolean): Toggles custom SVG HUD overlays.
- textTruncate (Boolean): Truncates strings vs wrapping to paragraph block.
- cameraFacing (String): 'environment' or 'user'.
- mirrorVideo (String|Bool): 'auto' (mirrors webcams/front-cameras flawlessly), true, or false.
- gpuTransform (Boolean): Applies
will-change. Keep false to prevent text blurring on high-end devices. Recommand first playing with overlayResolution
- enable3D (Boolean): Requires true to dynamically fetch Three.js from CDN.
- autoRotate3D (Boolean): Rotates models on Y-axis.
- rotateSpeed (Number): Turntable speed multiplier.
- parseParam (Array/String): e.g. ['id', 'user']. Extracts parameters natively to use in assetResolver
- uriPrefix (Array/String): e.g. 'nike'. Enables custom nike:payload extraction to force an overlay type or use in assetResolver
- assetResolver (Async Function): Receives payload, returns mapped URLs, Config Overrides, or JSON Cards.
- templates (Object): Maps IDs to hidden DOM elements for custom HTML rendering.
Lifecycle Events
- onDetect(parsedData): Fires the instant a payload is decoded and extracted, before the UI builds. Perfect for firing a background analytics pixel or triggering an auto-download.
- onTrackingRestored(): Fires when AR physically locks onto the square.
- onTrackingLost(): Fires when AR loses physical tracking.
- onClick(parsedData): Fires when the user taps anywhere on the AR overlay (excluding inner buttons/media controls which handle their own clicks natively). Great for navigating to an external product page.
- onError(err): Captures Camera, WebWorker, and Resolver failures gracefully.
Pro-Tips & Gotchas
- HTTPS Requirement: Modern browsers strictly require
https:// (or localhost) to access the getUserMedia camera API.
- CORS for 3D/Media: If you are loading
.glb or video files from an external server (like AWS S3 or a custom CDN), ensure your server is configured with Access-Control-Allow-Origin: *, otherwise the WebGL canvas will block the download for security reasons.
Physical QR Code Generator API
Don't use a third-party generator. Our static helper creates beautiful, branded physical anchors instantly. With maximum error correction (Level H) when adding an embedded logos.
const qr = await OmniSpatialQR.generate({
data: 'omni:promo1',
type: 'soft', // standard, soft, dots
size: 300,
color: '#18181b', // Dot color
bgColor: '#ffffff',// Background color
logo: '/logo.png' // Drops your brand into the center
});
document.body.appendChild(qr);