spectacles-ui-kit
Spectacles UI Kit — Programmatic UI Guide
While Lens Studio provides UI prefabs, you can construct complex interfaces entirely in TypeScript using the Spectacles UI Kit. This approach (sometimes called FastUI) is ideal for dynamic grids, scalable menus, and data-driven galleries.
Import Note: Components belong to
SpectaclesUIKit.lspkg.
Component Lifecycle (CRITICAL)
When creating UI Kit components via script, you must follow this exact lifecycle sequence or the layout will fail to render:
global.scene.createSceneObject()createComponent('TypeName')- Set physical size/dimensions
- Call
.initialize() - Set visual properties (appearance, margins)
- Bind interaction events
1. Creating a Frame (The Root Container)
The Frame component holds panels, adds a background plate, and handles focus behaviors.
import { Frame } from "SpectaclesUIKit.lspkg/Scripts/Components/Frame/Frame"
const frameObj = global.scene.createSceneObject("MainFrame")
const frameComp = frameObj.createComponent(Frame.getTypeName()) as any
// 1. Initialize Frame first (required by UIKit)
frameComp.initialize()
// 2. Set properties AFTER initialization
frameComp.innerSize = new vec2(24, 26) // Width, Height (cm)
// Appearance: "Large" (Far-field) or "Small" (Near-field)
frameComp.appearance = "Large"
// Auto-show/hide based on user gaze interaction
frameComp.autoShowHide = true
// Use the setFollowing method (do not set a property directly)
if (frameComp.setFollowing) {
frameComp.setFollowing(false)
}
2. Creating a Grid Layout
GridLayout automatically arranges child objects in rows and columns.
import { GridLayout } from "SpectaclesUIKit.lspkg/Scripts/Components/GridLayout/GridLayout"
const gridObj = global.scene.createSceneObject("GridLayout")
gridObj.setParent(frameObj)
const grid = gridObj.createComponent(GridLayout.getTypeName())
// Configure Grid geometry
grid.rows = 3
grid.columns = 3
// cellSize is a vec2 (width, height)
grid.cellSize = new vec2(4, 4)
// cellPadding is a vec4 (Left, Top, Right, Bottom)
grid.cellPadding = new vec4(0.5, 0.5, 0.5, 0.5)
// layoutBy: 0 = Row-first, 1 = Column-first
grid.layoutBy = 0
// Initialize (locks in the geometry)
grid.initialize()
3. Creating Buttons
You can create RectangleButton, CapsuleButton, or RoundButton.
import { RectangleButton } from "SpectaclesUIKit.lspkg/Scripts/Components/Button/RectangleButton"
const btnObj = global.scene.createSceneObject("Button_1")
btnObj.setParent(gridObj) // Add to grid
const button = btnObj.createComponent(RectangleButton.getTypeName()) as any
// 1. Set size BEFORE initialize!
// Note: For RoundButton use button.width = 4;
button.size = new vec3(4, 4, 1)
// 2. Initialize
button.initialize()
// 3. Configure styling post-initialization
button.renderOrder = 0 // matches parent frame
button.hasShadow = true
// Note: Button style (Primary, Secondary, Ghost) is handled in
// constructor/inspector. Changing it at runtime is unsupported.
// 4. Bind interactions
button.onTriggerUp.add(() => {
print("Button clicked!")
})
// Optional: Add a text label manually
const textObj = global.scene.createSceneObject("Label")
textObj.setParent(btnObj)
const textComp = textObj.createComponent("Component.Text")
textComp.text = "Click Me"
textComp.size = 12
textComp.horizontalAlignment = HorizontalAlignment.Center
textComp.verticalAlignment = VerticalAlignment.Center
4. Manual Layouts (Optional)
If GridLayout is too rigid, you can calculate positions manually for simple vertical or horizontal stacks:
// Example: Vertical Layout loop
const spacing = 1.5
const btnHeight = 2.5
for (let i = 0; i < 5; i++) {
const btnObj = createMyButton() // generic wrapper returning your button
const yPos = -i * (btnHeight + spacing)
btnObj.getTransform().setLocalPosition(new vec3(0, yPos, 0))
btnObj.setParent(menuContainer)
}
Common Gotchas
- Initialization Order: Passing properties to a UI element before
.initialize()rarely works. Set base dimensions usually before, then init, then appearance. - RoundButton Size: Uses
.width, not.size. - Corner Radius: Automatically calculated by the component based on size constraints. Don't overwrite it directly.
LayoutDirectionenum: If you lack the TS import forLayoutDirection.Row, just pass the integer0.- Text Sizing: Text components map
sizearbitrarily based on their transform hierarchy. 12-16 is a good starting point for button labels in a Large frame.
Reference Examples
- UIKitPatternGenerator.ts - An official comprehensive programmatic UI builder demonstrating all component types and lifecycle requirements.
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.
7spectacles-connected-lenses
Reference guide for real-time multiplayer AR on Spectacles using Connected Lenses and Spectacles Sync Kit — covering session creation/joining with joinOrCreateSession (including 'already-in-session' error handling), TransformSyncComponent for position/rotation replication, RealtimeStore for shared key-value state (max 512 bytes per key), NetworkEventSystem for one-shot broadcast events, EntityOwnership for physics authority, Lens Cloud for persistent cross-session data, and patterns for turn-based (Tic Tac Toe) and real-time physics (Air Hockey). Also covers late-joiner state sync, transform drift mitigation, and store size limits. Use this skill whenever multiple Spectacles users need to share AR objects or state — covering Tic Tac Toe, Air Hockey, Laser Pointer, High Five, Shared Sync Controls, Spectacles Sync Kit, and Think Out Loud samples.
5lens-studio-vfx
Reference guide for Lens Studio's VFX Graph particle system — covering VFXComponent setup (VFX asset vs Component relationship), reading and writing .asset.properties to pass data into a VFX graph at runtime (position, color, float, direction vectors), particle system inspector settings (emitter rate, lifetime, shape, blending, world vs local simulation space), spawning particles from script (trigger emission via properties), sending scene object transforms and screen-space data to VFX, and common VFX bugs (type mismatches on properties, asset null checks). Use this skill whenever a lens needs particle effects, camera-reactive or gesture-driven VFX, attaching effects to face/body tracking anchors, or connecting real-time data (position, audio levels, expression weights) into a VFX graph.
5