Skip to content

stretchable

The stretchable component enables interactive resizing of objects using a pinch gesture from hand tracking or trigger-based gestures from VR controllers.
It supports both uniform and non-uniform scaling and automatically determines whether the user is manipulating a corner of the object's bounding box.

The component listens to global gesture events such as hand-pinch-started, hand-pinch-moved, hand-pinch-ended, and their controller equivalents (controller-triggerdown, controller-move, controller-triggerup).
When a gesture begins, the component checks if the interaction point is close enough to one of the object's corners. Only the nearest valid corner activates the stretch interaction to prevent accidental resizing.

Two scaling modes are supported:

  • mode: "scale" – uniform scaling that preserves proportions, based on distance change from the object's center.
  • mode: "dimensions" – non-uniform scaling, allowing independent resizing along selected axes (dimensionAxes: ["x","y","z"]).

Scaling respects the configured minimum and maximum size limits, and works with both hands and controllers.

Tested on Meta Quest Pro (hands/controllers).

Example

Below is an example showing how to use the stretchable component in a scene. Try this demo with hand tracking enabled AR hardware (e.g., Meta Quest Pro) or with controllers.

js
import 'spatial-design-system/components/autoXr.js';
import 'spatial-design-system/components/hands.js';
import 'spatial-design-system/components/controllers.js';
import 'spatial-design-system/components/stretchable.js';
html
<a-scene auto-xr>
  <!-- This line enables hand tracking for both hands -->
  <a-entity id="rig" hands> </a-entity>
  <!-- or with controllers -->
  <a-entity id="rig" controllers> </a-entity>

  <!-- This enables stretchable interaction on the box when using dimensions mode -->
  <a-box
    position="-0.5 1.2 -0.7"
    width="0.5"
    height="0.5"
    depth="0.5"
    color="#03FCC6"
    stretchable="dimensionAxes: x, z"
  ></a-box>

  <!-- This enables stretchable interaction on the box when using scale mode -->
  <a-box
    position="0.5 1.2 -0.7"
    width="0.5"
    height="0.5"
    depth="0.5"
    color="#2196F3"
    stretchable="mode: scale; maxSize: 2; minSize: 0.5"
  ></a-box>
</a-scene>

Props

PropertyTypeDefaultDescription
modestring"dimensions"Stretch mode: "scale" for uniform scaling, "dimensions" for axis-specific scaling.
dimensionAxesarray["x","y","z"]Axes allowed for non-uniform scaling when mode: "dimensions".
maxSizenumber1.5Maximum allowed size multiplier applied to the original scale of the object.
minSizenumber0.5Minimum allowed size multiplier applied to the original scale of the object.
maxBoxTouchDistancenumber0.03Distance (in meters) within which a gesture must be to consider the object’s bounding box as “touching.”
maxCornerSelectDistancenumber0.06Distance threshold for selecting the nearest corner of the bounding box. Looser than maxBoxTouchDistance.

Events

EventParametersDescription
stretch-started{ handOrController, intersectionPoint, pinchState }Fired when a valid stretch gesture begins. Includes the input source (hand or controller), the world-space point of the initial pinch/touch, and an internal pinchState snapshot storing initial scale, axes, and corner selection data.
stretch-moved{ handOrController, currentPoint, newScale, pinchState }Fired continuously during stretching. Provides current world-space gesture position, the updated scale applied to the object, and the original pinchState used to compute scaling.
stretch-ended{ handOrController, finalScale, pinchState }Fired when the stretch gesture finishes. Contains the gesture source, the final applied scale, and the full pinchState from the interaction session.

Features

  • Automatically applies obb-collider to the entity for accurate bounding box detection.
  • Adds the interactable class to the entity so that it can be targeted by the hands component.

Usage Tips

  • Using hands: Press the index finger of the dominant hand in the corner of the object to activate the stretchable and pull to the side.
  • Using controllers: Press the trigger button of the controller pointing at the object corner to activate the stretchable and pull to the side.

Limitations

  • Component has fixed default values for minScaleFactor (0.5) and maxScaleFactor (1.5) that constrain scaling relative to the object's initial scale to prevent accidental infinite scaling. These factors are applied to each axis independently in both modes.
  • The stretchable can be combined with another component using pinch gesture (e.g., grabbable, hands-hoverable).
  • Must be used in conjunction with the hands component and a hand-tracking-enabled XR device or controllers component. It is not supported in mobile browsers.