spectacles-connected-lenses
Spectacles Connected Lenses — Reference Guide
Connected Lenses let multiple Spectacles users share a real-time AR session. The primary framework is the Spectacles Sync Kit, built on top of Lens Studio's Lens Cloud networking layer.
Official docs: Spectacles Home · Connected Lenses · Spectacles Sync Kit (under Spectacles Frameworks)
Architecture
User A (Spectacles) ──┐
├─── Lens Cloud (Snap servers) ─── Shared session state
User B (Spectacles) ──┘ (transforms, store, events)
The Sync Kit handles session creation/joining, object ownership, delta sync, and conflict resolution.
Sync Kit Setup
Add via Asset Library (search "Spectacles Sync Kit"). Installs: SyncKit prefab, RealtimeStore, and helper components.
- Drag the SyncKit prefab into your scene.
- Set a unique lens ID on the SyncKit component.
- Add
SyncEntity+TransformSyncComponentto objects that need to replicate.
Session Management
const connectedLensModule = require('LensStudio:ConnectedLensModule')
// Join or create a session — handles the "already in session" case automatically
connectedLensModule.joinOrCreateSession({}, (session, error) => {
if (error) {
print('Session error: ' + error)
// Common error codes:
// 'already_in_session' — user is already in a session from another lens
// 'session_not_found' — tried to join a session that no longer exists
return
}
print('Session joined. Users: ' + session.users.length)
})
// Listen for user join/leave after session is active
const session = connectedLensModule.getSession()
session.onUserJoined.add((user) => spawnUserAvatar(user))
session.onUserLeft.add((user) => despawnUserAvatar(user.userId))
// Identify the local user
const myUserId = session.localUser.userId
Transform Synchronisation
import { TransformSyncComponent } from 'SpectaclesSyncKit/Components/TransformSyncComponent'
// Simply moving the object causes TransformSyncComponent to broadcast the change.
// Remote clients receive position/rotation/scale updates automatically.
this.sceneObject.getTransform().setWorldPosition(newPos)
Sync Kit interpolates on remote clients so motion appears smooth.
RealtimeStore — Shared State
Size limit: each key-value pair is capped at 512 bytes. Store small values (indices, IDs, flags, short strings), not mesh data or large arrays.
import { RealtimeStore } from 'SpectaclesSyncKit/Core/RealtimeStore'
// The store is provided by the SyncKit prefab — get it from the SyncEntity, not as a singleton
@input realtimeStore: RealtimeStore
// Write
this.realtimeStore.putString('gameState', 'playing')
this.realtimeStore.putFloat('player1Score', 3)
this.realtimeStore.putBool('isPlayer1Turn', true)
// Read
const state = this.realtimeStore.getString('gameState')
// React to remote changes — always validate incoming values
this.realtimeStore.onValueChanged.add((key: string, value: any) => {
if (key === 'gameState') {
// Validate before acting — any client can write to the store
if (typeof value !== 'string') return
updateGameUI(value as string)
}
if (key === 'player1Score') {
const score = Number(value)
if (!isFinite(score) || score < 0 || score > 9999) return // reject invalid values
updateScoreboard(score)
}
})
// Restrict a key so only a specific user can write it
this.realtimeStore.setOwner('player1Score', myUserId)
Custom Networked Events
One-shot events broadcast to all clients (good for collisions, high fives, scoring):
import { NetworkEventSystem } from 'SpectaclesSyncKit/Core/NetworkEventSystem'
const net = NetworkEventSystem.getInstance()
net.send('SCORE', { userId: myUserId, points: 1 })
net.on('SCORE', (payload) => {
updateScoreboard(payload.userId, payload.points)
})
Turn-Based Pattern (Tic Tac Toe)
const MY_ID = connectedLensModule.getSession().localUser.userId
function isMyTurn(): boolean {
return this.realtimeStore.getString('currentPlayerId') === MY_ID
}
function onCellTapped(cellIndex: number): void {
if (!isMyTurn()) return
this.realtimeStore.putInt('cell_' + cellIndex, myPlayerIndex)
this.realtimeStore.putString('currentPlayerId', getOtherPlayerId())
checkWinCondition()
}
Real-Time Physics Pattern (Air Hockey)
One client acts as physics authority; others receive positions:
import { EntityOwnership } from 'SpectaclesSyncKit/Core/EntityOwnership'
const ownership = this.sceneObject.getComponent(EntityOwnership.getTypeName()) as EntityOwnership
if (ownership.isOwner()) {
runPhysicsUpdate() // simulate physics here
// TransformSyncComponent broadcasts result
} else {
physicsBody.bodyType = Physics.BodyType.Kinematic // just follow sync, don't simulate
}
Persistent Shared Data (Across Sessions)
RealtimeStore is ephemeral — lost when the last user leaves. For persistence:
const lensCloud = require('LensStudio:LensCloud')
lensCloud.put({ key: 'sharedNote', value: 'Hello AR!' }, 'public_all')
lensCloud.get('sharedNote', 'public_all', (result) => {
if (result.success) displayNote(result.value)
})
⚠️
public_allis visible to every lens. Any lens can read data stored at this access level. Useprivate_userfor anything user-specific andpublic_userfor data you intend to share only within your own lens.
Access levels: private_user, public_user, public_all.
Common Gotchas
joinOrCreateSessionis the recommended entry point — it handles "already in session" gracefully.session.localUseris the correct property for the local user (notactiveUser).- Last-write-wins for store conflicts — use Snap Cloud edge functions (see
spectacles-cloud) for authoritative server-side decisions. - Validate incoming RealtimeStore values before acting on them — any connected client can write any value. Always check type and range.
- RealtimeStore cap: 512 bytes per key — store small values (indices, IDs, flags), not large payloads.
- Design for latency — events can arrive late or out of order; never assume instant receipt.
- Sync Kit version must match your Lens Studio version — re-import after upgrading.
Reference Examples
- SyncEntityExample.ts - Official reference on setting up a generic
SyncEntity.
More from rolandsmeenk/lensstudioagents
lens-studio-scripting
Reference guide for the Lens Studio TypeScript component system — covering the @component, @input, @hint, @allowUndefined, and @label decorators, the BaseScriptComponent lifecycle (onAwake vs OnStartEvent, UpdateEvent, DelayedCallbackEvent one-shot and repeating timers, TurnOnEvent/TurnOffEvent, onDestroy), accessing components with getComponent (plus null-check patterns to fix 'cannot read property of null' errors), cross-TypeScript imports with getTypeName(), NativeLogger vs print, prefab instantiation (sync and async), SceneObject hierarchy queries, and enabling/disabling objects. Use this skill whenever writing or debugging any Lens Studio TypeScript script, wiring up scene objects, or fixing 'this is undefined' or null-reference errors — platform-agnostic (works for Spectacles and phone lenses).
12spectacles-lens-essentials
Reference guide for foundational Lens Studio patterns on Spectacles — covering the GestureModule (pinch down/up/strength, targeting, grab, phone-in-hand with correct TypeScript API), SIK components (PinchButton, DragInteractable, GrabInteractable, ScrollView), hand-tracking gestures, physics bodies/colliders/callbacks (including audio-on-collision), LSTween animation (position/scale/rotation/color tweens), prefab instantiation at runtime, materials (clone-before-modify), spatial anchors, on-device persistent storage (putString/getFloat), spatial images, and the Path Pioneer raycasting pattern. Use this skill for any Spectacles lens that needs interaction, motion, animation, physics, audio, or persistent local storage — including Essentials, Throw Lab, Spatial Persistence, Spatial Image Gallery, Path Pioneer, Public Speaker, Voice Playback, Material Library, and DJ Specs samples.
9lens-studio-world-query
Reference guide for world understanding and scoring in Lens Studio — covering WorldQueryModule HitTestSession (HitTestSessionOptions.filter for jitter smoothing, semantic surface classification for floor/wall/ceiling/table detection, null result handling, per-frame performance), SIK InteractionManager targeting interactor ray pattern, Physics.createGlobalProbe().rayCast for scene-collider hits with collision layer filtering, aligning objects to surface normals using quat.lookAt, and the LeaderboardModule (create/retrieve with TTL and OrderingType, submitScore, getLeaderboardInfo with UsersType.Global/Friends). Use this skill when detecting real floors/walls/tables to place AR content, raycasting for hover or interaction against scene objects, or adding a global in-lens leaderboard — differentiates from spectacles-lens-essentials (physics/SIK) and from spectacles-cloud (Supabase persistence).
8spectacles-cloud
Reference guide for Snap Cloud (Supabase-powered backend) in Spectacles lenses — covering Fetch API setup (requires Internet Access capability in Project Settings), Postgres REST queries with the anon key, Row Level Security policies, Realtime WebSocket subscriptions with correct postgres_changes event format and reconnect-on-sleep patterns, cloud storage uploads of base64 images captured by Spectacles, serverless Edge Functions, and companion web dashboard architecture. Use this skill whenever a lens needs persistent cloud data, needs to share data with a web app in real time, uploads captured images to a bucket, or calls a cloud function — covering Snap Cloud and World Kindness Day samples. Use spectacles-networking for plain REST calls to non-Snap backends, and spectacles-connected-lenses for in-session multiplayer state.
7lens-studio-materials-shaders
Reference guide for materials and shaders in Lens Studio — covering runtime material property changes (clone-before-modify, mainPass.baseColor, mainPass.opacity, mainPass.baseTex), blend modes (Normal/Alpha/Add/Screen/Multiply), depth and cull settings (depthTest, depthWrite, twoSided, cullMode), render order, material variants, assigning textures and render targets, reading and writing RenderTarget textures for post-processing, the graph-based Material Editor node system, custom shader graph nodes, and common shader pitfalls. Use this skill for any lens that needs to change material colors or textures at runtime, implement custom visual effects with shaders, set up post-processing render pipelines, chain render targets, or debug material/blend-mode issues — covering MaterialEditor, Drawing, and HairSimulation examples.
6spectacles-networking
Reference guide for the Lens Studio Fetch API and WebView component in Spectacles lenses — covering InternetModule (Lens Studio 5.9+), Fetch API via internetModule.fetch(Request) with bytes/text/json response handling, performHttpRequest, Internet Access capability, GET/POST requests, custom headers, Bearer auth, polling, timeouts, CORS/HTTPS, WebSocket and RemoteMediaModule for media from URLs, and bidirectional WebView messaging. Use this skill for any lens that calls a REST API, polls a JSON endpoint, loads remote images, embeds a webpage, or talks to a custom backend — including the Fetch sample. Use spectacles-ai for LLM/RSG calls, or spectacles-cloud for Supabase/Snap Cloud integration.
6