While customizing the default handles goes a long way, sometimes it is necessary to add your own implementation.

There are two ways to do this:

  1. create new instances of the default handles and attach them to the controls object via controls.setupHandle.
  2. implement the abstract group classes to create the handles and then use controls.setupHandle

Note that the rules of modifying the up and parallel vectors when modifying the default handles, applies to custom handles as well.

Custom handles by instantiating default handles

import { Translation, Rotation } from "three-freeform-controls";
// ...
const controls = ControlsManager.anchor(box);

// create custom handles
const translationHandle = new Translation("cyan");
const rotationHandle = new Rotation("pink"); = "TranslationHandle"; = "RotationHandle";

// modify the handles
translationHandle.rotateZ(-Math.PI / 2);

// modify the "up" vector to point perpendicular to the desired interaction plane of the object
translationHandle.up = new THREE.Vector3(0, 1, 0);
rotationHandle.up = new THREE.Vector3(0, 0, 1);

// only for handles of type "TranslationGroup",
// modify the "parallel" vector to point to the direction in which the handle is pointing
translationHandle.parallel = new THREE.Vector3(1, 0, 0);

// setup the custom handles

An example of the above snippet can be accessed below.

View example

Custom handles by extending handles groups

The following handles groups are available:

import { PickGroup, PickPlaneGroup, RotationGroup, TranslationGroup } from "three-freeform-controls";

Extending any of these groups requires implementing the following properties:

  1. getInteractiveObjects: returns an array of the all the objects that are interactive in the handle
  2. setColor: sets the color for the handle

In addition, the TranslationGroup also requires implementing a parallel vector property on the handle.

Once the custom handle class has been created, it can be instantiated and the up and parallel vectors should be handled accordingly as above.

example custom rotation handle

In the below example, we create a rotation handle with these properties:

  1. an interactive ball surrounded by a non-interactive ring
  2. the ball is 1.5 units above the object
  3. the rotation is along the z-axis
import { RotationGroup } from "three-freeform-controls";
// ...
const controls = ControlsManager.anchor(box);

class CustomRotation extends RotationGroup {
  constructor(color) {
    this.ball = new THREE.Mesh(
      new THREE.SphereGeometry(0.1, 32, 32),
      new THREE.MeshNormalMaterial()
    this.ring = new THREE.Mesh(
      new THREE.RingGeometry(0.2, 0.3, 8),
      new THREE.MeshBasicMaterial({ color, side: THREE.DoubleSide })

    // add all the visual elements to the handles object

  getInteractiveObjects = () => {
    // only return the interactive children objects in the handles object
    return [this.ball];

  setColor = color => {

// the rest of the flow is same as previous examples
const customRotationHandle = new CustomRotation("yellow"); = "Custom Rotation Handle";
customRotationHandle.position.set(0, 1.5, 0);
customRotationHandle.up = new THREE.Vector3(0, 0, 1);

The above illustrated example can be accessed below.

View example

Note that the centre of rotation remains the position of the controls object, and not the position of the individual handles.

Next section: Translation Limit