[Solved] Can I use OrbitControls with Browserify?

Hi, guys!

I want to use the require keyword for a client side via Browserify:

const THREE = require("three");
const OrbitControls = require("");

To save a space on my hard drive I keep all NPM modules on my laptop in the node_modules folder in one place here: E:\_Projects My project is here: E:\_Projects\ThreeJS\browserify\threejs-orbitcontrols-browserify-js

What I must write here require("") to get the OrbitControls:

const THREE = require("three");
const OrbitControls = require("");

I want to place my project on GitHub. Every beginner student can download a project, can write npm i to install packages in local node_modules, can write npm run debug to build the project and run it without problems.

Browserify bundles this, but I do not like this path:

const OrbitControls = require("E:/_Projects/node_modules/three/examples/js/controls/OrbitControls");

But this does not work:

image

When I write jsm instead of js in this path E:/_Projects/node_modules/three/examples/js/controls/OrbitControls:

const OrbitControls = require("E:/_Projects/node_modules/three/examples/jsm/controls/OrbitControls");

Browserify cannot bundle it:

image

I do not need to write a full path. I can write a relative path:

const OrbitControls = require("three/examples/jsm/controls/OrbitControls");

But I still get this error:

When I use import:

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

I get the same error but in main.js at this time:

> browserify --debug src/main.js -o public/js/bundle.js

Error: Parsing file E:\_Projects\ThreeJS\browserify\threejs-orbitcontrols-browserify-js\src\main.js: 'import' and 'export' may appear only with 'sourceType: module' (5:0)

I see that OrbitControls.js contains the import keyword ant it is mean that Three.js will not work with ES5, only with ES6+:

import {
	EventDispatcher,
	MOUSE,
	Quaternion,
	Spherical,
	TOUCH,
	Vector2,
	Vector3
} from '../../../build/three.module.js';

var OrbitControls = function ( object, domElement ) {

I tried to use import with Browserify. I added packages:

  "dependencies": {
    "@babel/core": "^7.15.5",
    "@babel/preset-env": "^7.15.6",
    "babel-core": "^6.26.3",
    "babel-preset-env": "^1.7.0",
    "babelify": "^10.0.0",
    "browser-sync": "^2.27.5",
    "browserify": "^17.0.0"
  },
  "babel": {
    "presets": [
      "@babel/preset-env"
    ]
  },
  "browserify": {
    "transform": [
      [
        "babelify",
        {
          "presets": [
            "@babel/preset-env"
          ],
          "sourceMaps": true,
          "global": true,
          "ignore": []
        }
      ]
    ]
  }

I import like this:

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

But I have the same error:

image

browserify is an ancient pre esm-era tool, i can’t imagine that is the best tool for what you’re doing unless you want to sink a weekend. can’t you use any other bundler that would take that with complete ease? they don’t care if it’s import or require. vite, parcel, or even just online codesandbox, you’d have what you want under 10 seconds.

they have dev, build, test inbuilt. your students just have to git clone url and npm install it.

for something really simple just type npm init vite these modern tools hardly “bundle”, they use modern web standards (esm) and just manage modules, usually with esbuild underneath. they are also very fast. vite (npm run dev) is up in milliseconds.

Because I do not want to spend my time to study a new bundler. I use Browserify with RequireJS for TypeScript and Browserify for JavaScript a few years. Sometimes I need to use ES5 to run my applications on old systems like Windows XP. For this reason I use a ES5 module for TypeScript compiler. It is more simpler to use another engine. BabylonJS works with Browserify very well: GitHub - 8Observer8/babylonjs-browserify-js and with Phaser: GitHub - 8Observer8/phaser-browserify-js It works with JavaScript ES5 and RequireJS/AMD/TypeScript.

Three.js works with Browserify too: GitHub - 8Observer8/threejs-browserify-js But OrbitCotrols does not work with Browserify.

these new tools just understand your code. es5, esm, require, typescript, babel, they just deal with it. there is no configuration either, you don’t spend time on it. you are trying to make a tool that is half a decade old work with modernity, that will require some struggle and study to make that work.

don’t take it in the wrong way please, it was just a suggestion. :relaxed: by all means use browserify, i’m sure you’ll find a way to make orbitcontrols work.

I will try to change OrbitCotrols.js from ES6 to ES5. I see that OrbitCotrols.js is already in ES5 because it uses var:

var OrbitControls = function ( object, domElement ) {

But it has ES6 import:

import {
	EventDispatcher,
	MOUSE,
	Quaternion,
	Spherical,
	TOUCH,
	Vector2,
	Vector3
} from '../../../build/three.module.js';

I changed this code to:

var {
	EventDispatcher,
	MOUSE,
	Quaternion,
	Spherical,
	TOUCH,
	Vector2,
	Vector3
} = require("../../../build/three.module.js");

It uses export:

export { OrbitControls, MapControls };

I changed this code to:

exports.OrbitControls = OrbitControls;
exports.MapControls = MapControls;

Now errors are here in three.module.js:

> browserify --debug src/main.js -o public/js/bundle.js

SyntaxError: 'import' and 'export' may appear only with 'sourceType: module' (49040:0) while parsing E:\_Projects\node_modules\three\build\three.module.js while parsing file: E:\_Projects\node_modules\three\build\three.module.js

But the three.module.js file has 465 exported objects. I wrote a script in Python:

content = "ACESFilmicToneMapping, AddEquation, AddOperation, ..."
arr = content.split(", ")
len(arr) # 465

I will write this script later. It will generate these lines:

exports.ACESFilmicToneMapping = ACESFilmicToneMapping;
exports.AddEquation = AddEquation;
exports.AddOperation = AddOperation;
...

I wrote this script in Python:

content = "ACESFilmicToneMapping, AddEquation, AddOperation, ..."

arr = content.split(", ")

f = open("result.txt", "w")

for v in arr:
    parts = v.split(" ")
    if len(parts) != 3:
        f.write(f"exports.{v} = {v};" + "\n")

f.close()

I get exports:

exports.ACESFilmicToneMapping = ACESFilmicToneMapping;
exports.AddEquation = AddEquation;
exports.AddOperation = AddOperation;
exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode;
exports.AdditiveBlending = AdditiveBlending;
...

Bundle was created. But I get this error:

three.module.js:49030 WARNING: Multiple instances of Three.js being imported.

main.js:37 Uncaught TypeError: THREE.OrbitControls is not a constructor

image

I tried with THREE:

controls = new THREE.OrbitControls(camera, renderer.domElement);

and without THREE:

controls = new OrbitControls(camera, renderer.domElement);

It the same.

const THREE = require("three");
const OrbitControls = require("three/examples/jsm/controls/OrbitControls.js");

let camera, scene, renderer;
let geometry, meterial, mesh;
let controls;

window.onload = () =>
{
    init();
    animate();
    window.onresize = () => { onResize(); };
};

function init()
{
    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
    camera.position.z = 1;

    scene = new THREE.Scene();

    geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
    material = new THREE.MeshNormalMaterial();

    mesh = new THREE.Mesh(geometry, meterial);
    scene.add(mesh);

    const canvas = document.getElementById("renderCanvas");
    renderer = new THREE.WebGLRenderer({ antialias: true, canvas: canvas });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    controls = new THREE.OrbitControls(camera, renderer.domElement);
}

function animate()
{
    requestAnimationFrame(animate);

    // mesh.rotation.x += 0.01;
    // mesh.rotation.y += 0.02;

    controls.update();

    renderer.render(scene, camera);
}

function onResize()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}
{
  "name": "threejs-orbitcontrols-browserify-js",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "clear": "del /f /q /s .\\public\\js\\*.*",
    "del-bundle": "del /f /q /s .\\src\\bundle.js",
    "bundle-debug": "browserify --debug src/main.js -o public/js/bundle.js",
    "bundle-release": "browserify src/main.js -o src/bundle.js",
    "uglify": "uglifyjs src/bundle.js -o public/js/bundle.min.js",
    "debug": "npm run bundle-debug",
    "release": "npm run clear && npm run bundle-release && npm run uglify && npm run del-bundle"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

I solved this error! Instead of this line:

const OrbitControls = require("three/examples/jsm/controls/OrbitControls.js");

I must write:

const { OrbitControls } = require("three/examples/jsm/controls/OrbitControls.js");

And it works:

orbit-controls

I still have this warning:

image

But I think it is not important.