CtrlK
BlogDocsLog inGet started
Tessl Logo

iwsdk-ui-panel

Develop and iterate on IWSDK UI panels efficiently. Use when working on PanelUI components, debugging UI layout, or improving UI design in IWSDK applications.

64

Quality

76%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Optimize this skill with Tessl

npx tessl skill review --optimize ./.claude/skills/iwsdk-ui-panel/SKILL.md
SKILL.md
Quality
Evals
Security

IWSDK UI Panel Development Workflow

This skill teaches the efficient workflow for developing UI panels in IWSDK applications using temporary ScreenSpace positioning and backdrop techniques.

Quick Iteration Workflow

When working on a UI panel, follow these steps for rapid iteration:

1. Add Temporary ScreenSpace Component

Temporarily add the ScreenSpace component to your PanelUI entity to make it fill the 2D screen during development:

import { ScreenSpace } from '@iwsdk/core';

world
  .createTransformEntity(panelHolder)
  .addComponent(PanelUI, {
    config: '/ui/your-panel.json',
    maxWidth: 1.0,
    maxHeight: 0.5,
  })
  .addComponent(ScreenSpace, {
    width: '90vw', // Fill 90% of viewport width
    height: '90vh', // Fill 90% of viewport height
    top: '5vh', // Center with 5% margins
    left: '5vw',
  });

Important: This is temporary for development only. Remove before production.

2. Create a Clean Backdrop

Create a solid color backdrop far from your gameplay area for clean UI visibility:

const backdrop = new Mesh(
  new BoxGeometry(20, 20, 0.1),
  new MeshBasicMaterial({ color: 0x1a1a2e }),
);
backdrop.position.set(0, 0, -50); // Far from gameplay
scene.add(backdrop);

3. Position Camera Close to Backdrop

Move the camera very close to the backdrop (within 0.5m) to eliminate background distractions:

// Position camera very close to backdrop for clean UI development
camera.position.set(0, 0, -49.5); // Just 0.5m from backdrop at z=-50
camera.lookAt(0, 0, -50);

Why close? The backdrop must fill the entire field of view to block out the 3D scene. Being far away (50m) won't work - you'll still see the environment around the edges.

4. Iterate with Screenshots

Now you can rapidly iterate on your UI:

  1. Make changes to your .uikitml file
  2. Take a screenshot to see the result against a clean backdrop
  3. The UI fills most of the screen, making it easy to see details like:
    • Border colors and thickness
    • Padding and spacing
    • Text alignment and sizing
    • Color contrast
    • Overall layout

The ScreenSpace component makes the panel "follow" the camera, so it appears as a 2D overlay on your backdrop.

5. Test in VR

When you enter VR mode:

  • The ScreenSpace component automatically detaches the panel from the camera
  • The panel returns to its original 3D world space position
  • Your gameplay is unaffected

This dual-mode behavior is handled automatically by the ScreenSpaceUISystem.

Understanding UIKit Size Signals

UIKit components expose size information through signals. Log these to debug layout issues:

const document = PanelDocument.data.document[entity.index];

console.log('computedSize:', document.computedSize); // Intrinsic size in cm
console.log('targetSize:', document.targetSize); // Target size in meters
console.log('rootElement.size.value:', document.rootElement?.size?.value);
console.log('document.scale:', document.scale); // Applied scale

Understanding the output:

  • computedSize: UIKit's rendered size in centimeters (based on your CSS)
  • targetSize: The requested size in meters (from PanelUI maxWidth/maxHeight or ScreenSpace constraints)
  • document.scale: Uniform scale factor applied to fit target while preserving aspect ratio

Example output:

computedSize: { width: 100, height: 50 }         // 100cm × 50cm
targetSize: { width: 0.274, height: 0.168 }      // 0.274m × 0.168m
document.scale: { x: 0.274, y: 0.274, z: 0.274 } // Scaled down by 0.274x

ScreenSpace Component Reference

The ScreenSpace component positions panels using CSS-like properties:

.addComponent(ScreenSpace, {
  width: '90vw',      // CSS size: px, vw, vh, %, auto
  height: '90vh',     // CSS size: px, vw, vh, %, auto
  top: '5vh',         // CSS position: px, %, vh, auto
  bottom: 'auto',     // CSS position: px, %, vh, auto
  left: '5vw',        // CSS position: px, %, vw, auto
  right: 'auto',      // CSS position: px, %, vw, auto
  zOffset: 0.2,       // Distance in meters from camera (default: 0.2m)
});

How it works:

  • In desktop mode: Panel attaches to camera at zOffset distance, using CSS layout
  • In VR mode: Panel detaches from camera, returns to world space position
  • Automatic switching handled by ScreenSpaceUISystem

Common Workflow Tips

Centering Content

Use flexbox in your UIKitML for centered layouts:

.container {
  display: flex;
  flex-direction: column; /* Stack vertically */
  justify-content: center; /* Center vertically */
  align-items: center; /* Center horizontally */
}

Sharp Borders

Set border-radius: 0 for square edges that align with grid systems:

.panel {
  border-radius: 0; /* Square edges */
  border-width: 0.15;
  border-color: #27272a;
}

UIKit Units

Remember: UIKit uses centimeters for sizing, world space uses meters:

  • width: 100 in UIKitML = 100cm = 1.0m
  • maxWidth: 1.0 in PanelUI = 1.0 meter

Cleanup Before Production

Before committing or going to production:

  1. Remove ScreenSpace component from your entity
  2. Remove or reposition backdrop if not needed for gameplay
  3. Restore camera position to gameplay view
  4. Remove debug logging of size signals

The panel will remain at its world space position defined by the entity's transform.

Example: Complete Development Setup

// 1. Enable spatialUI feature
World.create(container, {
  features: { spatialUI: true },
}).then((world) => {
  const { scene, camera } = world;

  // 2. Create backdrop for UI development
  const backdrop = new Mesh(
    new BoxGeometry(20, 20, 0.1),
    new MeshBasicMaterial({ color: 0x1a1a2e }),
  );
  backdrop.position.set(0, 0, -50);
  scene.add(backdrop);

  // 3. Position camera close to backdrop
  camera.position.set(0, 0, -49.5);
  camera.lookAt(0, 0, -50);

  // 4. Create your UI panel with ScreenSpace
  const panelHolder = new Group();
  panelHolder.position.set(0, 1.5, -1.0); // World space position for VR
  scene.add(panelHolder);

  world
    .createTransformEntity(panelHolder)
    .addComponent(PanelUI, {
      config: '/ui/my-panel.json',
      maxWidth: 1.0,
      maxHeight: 0.5,
    })
    .addComponent(ScreenSpace, {
      // TEMPORARY for development
      width: '90vw',
      height: '90vh',
      top: '5vh',
      left: '5vw',
    });
});

Troubleshooting

Panel not filling screen:

  • Check ScreenSpace width/height values
  • Verify UIKitML doesn't have fixed small dimensions

Background still visible:

  • Camera too far from backdrop - move closer (within 0.5m)
  • Backdrop too small - increase size to 20×20 or larger

Panel doesn't return to world space in VR:

  • Verify spatialUI: true in World.create features
  • Check that ScreenSpaceUISystem is running

Size signals showing unexpected values:

  • UIKit uses cm, world space uses meters (100cm = 1m)
  • Check if aspect ratio constraints are being applied
Repository
facebook/immersive-web-sdk
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.