Skip to main content

Smart Object API Reference

The Smart Object API provides programmatic control over interactive 3D models in Fruit3D. Scripts access this API through the SmartObject global or the useSmartObject() hook.

For usage examples, see the Smart Object Scripting Guide.

SmartObjectApiV0

The main API interface exposed to scripts.

interface SmartObjectApiV0 {
version: "0.1";
modelId: string;

// 3D Object Access
getRoot(): Object3D | null;
getNodeById(nodeId: string): Object3D | null;
getNodeId(obj: Object3D): string | null;

// State Management
getActiveStateName(): string | undefined;
getStateNames(): string[];
setActiveStateByName(name: string): void;

// Metadata
getScriptName(): string;

// Selection
getSelection(): { nodeId: string; object: Object3D } | null;
setSelection(nodeId: string | null): void;

// Reference Points
getPoint(id: number): { x: number; y: number; z: number } | null;
getPoints(): Array<{ id: number; x: number; y: number; z: number }>;

// Event Listeners
on<K extends SmartObjectEventName>(
event: K,
handler: SmartObjectEventHandler<K>
): () => void;
}

Properties

version

version: "0.1"

The API version string. Currently "0.1".

modelId

modelId: string

The unique identifier of the current model.

Methods

getRoot()

getRoot(): Object3D | null

Returns the root Three.js Object3D of the loaded model, or null if not available.

Example:

const root = SmartObject.getRoot();
if (root) {
root.traverse((child) => {
console.log(child.name);
});
}

getNodeById()

getNodeById(nodeId: string): Object3D | null

Finds a 3D object by its node ID.

Parameters:

NameTypeDescription
nodeIdstringThe JSON path identifier (e.g., "0", "0.1", "0.1.2")

Returns: The Three.js Object3D if found, or null.

Example:

const door = SmartObject.getNodeById("0.1");
if (door) {
door.rotation.y = Math.PI / 2;
}

getNodeId()

getNodeId(obj: Object3D): string | null

Gets the node ID for a given Three.js object.

Parameters:

NameTypeDescription
objObject3DA Three.js object from the model

Returns: The node ID string, or null if the object doesn't have one.

Example:

SmartObject.on("click", ({ object }) => {
const nodeId = SmartObject.getNodeId(object);
console.log("Clicked:", nodeId);
});

getActiveStateName()

getActiveStateName(): string | undefined

Returns the name of the currently active state, or undefined if the state has no name.

Example:

const currentState = SmartObject.getActiveStateName();
console.log("Current state:", currentState);

setActiveStateByName()

setActiveStateByName(name: string): void

Switches to a different state by its name.

Parameters:

NameTypeDescription
namestringThe name of the state to switch to

Note: This method only works in preview mode. It has no effect in edit mode. If no state matches the given name, the call is ignored.

Example:

// Toggle between states
const current = SmartObject.getActiveStateName();
SmartObject.setActiveStateByName(current === "open" ? "closed" : "open");

getStateNames()

getStateNames(): string[]

Returns an array of all defined state names.

Returns: An array of state name strings. States without names are excluded.

Example:

const states = SmartObject.getStateNames();
console.log("Available states:", states); // e.g. ["open", "closed"]

getScriptName()

getScriptName(): string

Returns the display name of the current script.

Example:

const name = SmartObject.getScriptName();
console.log("Running script:", name);

getSelection()

getSelection(): { nodeId: string; object: Object3D } | null

Returns the currently selected node, if any.

Returns: An object with nodeId and object properties, or null if nothing is selected.

Example:

const selection = SmartObject.getSelection();
if (selection) {
console.log("Selected:", selection.nodeId);
}

setSelection()

setSelection(nodeId: string | null): void

Programmatically sets or clears the selection.

Parameters:

NameTypeDescription
nodeIdstring | nullThe node ID to select, or null to clear selection

Note: This method only works in preview mode.

Example:

// Select a node
SmartObject.setSelection("0.1");

// Clear selection
SmartObject.setSelection(null);

on()

on<K extends SmartObjectEventName>(
event: K,
handler: SmartObjectEventHandler<K>
): () => void

Registers an event listener.

Parameters:

NameTypeDescription
event"click" | "stateChanged"The event type to listen for
handlerfunctionThe callback function

Returns: An unsubscribe function. Call it to remove the listener.

Example:

React.useEffect(() => {
const unsubscribe = SmartObject.on("click", (payload) => {
console.log("Clicked:", payload.nodeId);
});

return unsubscribe; // Clean up on unmount
}, []);

getPoint()

getPoint(id: number): { x: number; y: number; z: number } | null

Returns the position of a reference point by its ID, or null if the point doesn't exist. Users can place numbered reference points (1-32) in the 3D scene.

Parameters:

NameTypeDescription
idnumberThe reference point ID (1-32)

Returns: An object with x, y, z coordinates, or null.

Example:

const pt = SmartObject.getPoint(1);
if (pt) {
object.position.set(pt.x, pt.y, pt.z);
}

getPoints()

getPoints(): Array<{ id: number; x: number; y: number; z: number }>

Returns all reference points with their IDs and positions.

Returns: An array of point objects, each with id, x, y, z.

Example:

const points = SmartObject.getPoints();
points.forEach((pt) => {
console.log(`Point ${pt.id}: (${pt.x}, ${pt.y}, ${pt.z})`);
});

Events

click

Fired when the user clicks on a part of the model.

Payload:

type SmartObjectClickPayload = {
nodeId: string; // The ID of the clicked node
object: Object3D; // The clicked Three.js object
};

Example:

SmartObject.on("click", ({ nodeId, object }) => {
console.log("Clicked node:", nodeId);
console.log("Object name:", object.name);
});

stateChanged

Fired when the active state changes.

Payload:

type SmartObjectStateChangedPayload = {
stateName: string; // The name of the new state
previousStateName: string; // The name of the previous state
};

Example:

SmartObject.on("stateChanged", ({ stateName, previousStateName }) => {
console.log(`Changed from "${previousStateName}" to "${stateName}"`);
});

Hooks

useSmartObject()

function useSmartObject(): SmartObjectApiV0

React hook that returns the Smart Object API. Use this inside React components for reactive access. When the backing context changes, components using useSmartObject() will re-render.

Use useSmartObject() (rather than the SmartObject global) when you need the API inside React lifecycle hooks like useEffect, useCallback, or useMemo, so that your component correctly re-subscribes if the API instance changes.

Example:

import React, { useState, useEffect } from "react";
import { useFrame } from "@react-three/fiber";
import { useControls } from "leva";

export default function ClickHighlight() {
const api = useSmartObject();
const [selectedId, setSelectedId] = useState<string | null>(null);
const { speed } = useControls({ speed: { value: 1, min: 0, max: 5 } });

useEffect(() => {
return api.on("click", ({ nodeId }) => {
setSelectedId(nodeId);
api.setSelection(nodeId);
});
}, [api]);

useFrame((_, delta) => {
if (selectedId) {
const obj = api.getNodeById(selectedId);
if (obj) obj.rotation.y += delta * speed;
}
});

return null;
}

Available Modules

Scripts can import from these modules (transformed to global variable lookups at compile time):

ModuleImport ExamplesDescription
reactimport React, { useState, useEffect } from "react"React 19 and all hooks
threeimport * as THREE from "three"Three.js 0.181
@react-three/fiberimport { useFrame, useThree } from "@react-three/fiber"R3F hooks
@react-three/dreiimport { Html, Text } from "@react-three/drei"Drei helpers
levaimport { useControls } from "leva"GUI controls for debugging

The SmartObject global and useSmartObject() hook are always available without importing.

Supporting Types

SceneState

Defines a named state with object transforms.

type SceneState = {
id: string;
name?: string;
transforms: Record<string, ObjectTransform>;
};

ObjectTransform

Transform data for a single object.

type ObjectTransform = {
position: [number, number, number];
rotation: [number, number, number];
scale: [number, number, number];
};

SceneEvent

Defines a click-triggered event.

type SceneEvent = {
id: string;
name?: string;
enabled: boolean;
appliesToStateId?: string;
trigger: SceneEventTrigger;
action: SceneEventAction;
};

SceneEventTrigger

type SceneEventTrigger = {
type: "click";
target: {
kind: "nodeId";
id: string;
};
};

SceneEventAction

type SceneEventAction = {
type: "switch_state";
stateId: string;
};

Node IDs

Node IDs are JSON path strings that identify objects in the model hierarchy:

  • "0" - First child of root
  • "0.1" - Second child of first child
  • "0.1.2" - Third child of "0.1"

These IDs are stored in userData.__jsonPath on each object and are automatically generated when the model loads.

Script Requirements

  1. Default export required - Scripts must export a React component as default
  2. Imports from known modules - Scripts may import from: react, three, @react-three/fiber, @react-three/drei, and leva. These are transformed into global variable lookups at compile time.
  3. No unknown modules - Importing from any other module is rejected
  4. No dynamic imports - require() and import() are not allowed
  5. Preview mode only - setActiveStateByName() and setSelection() only work in preview mode