Theme:

Align

This aligns / pins a plane to a fixed screen position. This is useful for making responsive user interface elements which adjust to the window size and device pixel ratio.

UI.Align(group, plane, marginPx, x, y, distance);

If the plane is the root of your UI, you can pass it in like:

UI.Align(plane, plane, marginPx, x, y, distance);

It's recommended to disable this when in XR, as it locks to the camera's position.

Aligned UI keeps the same apparent screen size when the camera FOV or zoom changes. A 50-degree FOV is used as the reference: the group's authored scale is unchanged at 50 degrees and compensated at other FOVs. Compensation does not accumulate when UI.Align is called every frame, and caller-authored scale changes remain supported.

What controls the displayed size?

UI.Align deals with three different kinds of sizing, and they are easy to mix up:

  • A UI.Panel's pixelWidth, pixelHeight, and dpr control CSS layout and texture resolution.
  • marginPx controls the panel's inset from the chosen screen edge in viewport pixels.
  • group.scale controls how large the aligned panel actually appears. The distance from the camera also affects its apparent size.

In particular, panel texture pixels are not screen pixels. Reducing a panel from 720 × 480 to 360 × 240 does not make it half as large on screen when its aspect ratio and object scale stay the same. It mainly gives the UI half as many pixels to render with, which makes text and edges blurrier.

Keep the panel resolution high and change its scale when you want a smaller HUD:

const hud = new UI.Panel(720, 480, 2);
hud.scale.set(0.1, 0.1, 0.1);
await hud.init();

this.update = () => {
  UI.Align(hud, hud.hitbox, 20, "center", "bottom", 0.25);
};

UI.Align treats the scale you set as the authored base size, then compensates it when the camera FOV changes. If you change the scale later, the new value becomes the base size on the next alignment call.

Example

this.update = () => {
  // Each frame the element is aligned with the camera / viewport view
  UI.Align(this, this.plane, 10, "left", "center", 0.2);
};

Signature

UI.Align(
  group: U.Object3D,
  plane: U.Mesh,
  marginPx: number = 10,
  x: 'left' | 'center' | 'right',
  y: 'top'  | 'center' | 'bottom',
  distance: number = 0.25 // distance from the camera on the Z axis
): void