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:
| Name | Type | Description |
|---|---|---|
nodeId | string | The 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:
| Name | Type | Description |
|---|---|---|
obj | Object3D | A 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:
| Name | Type | Description |
|---|---|---|
name | string | The 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:
| Name | Type | Description |
|---|---|---|
nodeId | string | null | The 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:
| Name | Type | Description |
|---|---|---|
event | "click" | "stateChanged" | The event type to listen for |
handler | function | The 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:
| Name | Type | Description |
|---|---|---|
id | number | The 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):
| Module | Import Examples | Description |
|---|---|---|
react | import React, { useState, useEffect } from "react" | React 19 and all hooks |
three | import * as THREE from "three" | Three.js 0.181 |
@react-three/fiber | import { useFrame, useThree } from "@react-three/fiber" | R3F hooks |
@react-three/drei | import { Html, Text } from "@react-three/drei" | Drei helpers |
leva | import { 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
- Default export required - Scripts must export a React component as default
- Imports from known modules - Scripts may import from:
react,three,@react-three/fiber,@react-three/drei, andleva. These are transformed into global variable lookups at compile time. - No unknown modules - Importing from any other module is rejected
- No dynamic imports -
require()andimport()are not allowed - Preview mode only -
setActiveStateByName()andsetSelection()only work in preview mode