Welcome to whs.js docs!

whs.js is a framework for 3D web apps built with Three.js technology.

# Install npm version
$ npm install whs

It implements a core with component system and plugin support for fast development of 3D scene with physics.

Automizing your web app with whitestorm is fast and comfortable. This engine has physics support implemented by custom Physi.js library, which is much faster than others. Framework provides extended component control and high frame rate, because it uses WebWorkers technology for multithreading.

const app = new WHS.App([
  new WHS.ElementModule(), // Apply to DOM.
  new WHS.SceneModule(), // Create a new THREE.Scene and set it to app.
 
  new WHS.DefineModule('camera', new WHS.PerspectiveCamera({ // Apply a camera.
    position: new Vector3(0, 0, 50)
  })),
 
  new WHS.RenderingModule({bgColor: 0x162129}), // Apply THREE.WebGLRenderer
  new WHS.ResizeModule() // Make it resizable.
]);
 
app.start(); // Run app.

> featured projectsGenerated by JSDoc 3.4.3 on Fri Jun 30 2020 19:28:00 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

Basic scene

To create whitestorm.js app you should make a basic HTML document with htmlhead and body tags. Next step is to include Whitestorm.js to the document and main app script file. You can do it simply using script tag.

Try this helloworld demo online.

App

The first thing you should setup is the App object. When you do this, you do multiple things at once:

  • Setup THREE.Scene
  • Make a perspective camera and add it to the scene.
  • Apply background and other renderer options.
  • Set auto resize (in addition).
const app = new WHS.App([
  new WHS.ElementModule(),
  new WHS.SceneModule(),
 
  new WHS.DefineModule('camera', new WHS.PerspectiveCamera({
    position: new THREE.Vector3(0, 0, 50)
  })),
 
  new WHS.RenderingModule({bgColor: 0x162129}),
  new WHS.ResizeModule()
]);

> Loops & 3D Animation

FAQ

Q: Why is the color in Hexadecimal format?

A: Simply following Three.js’ best practice.Generated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:46 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

Loops & 3D Animation

You don’t need to write animate() function like you do in Three.js. The problem is that bigger you app become, the bigger your animate() function is.

Here comes WHS.Loop class. With just a few lines of code you can your own mini-animate() function:

const app = new WHS.App([
  // ...
]);
 
new WHS.Loop(() => {
  box.rotation.y += 0.02;
}).start(app);

And you can make loop temporary like that:

const loop = new WHS.Loop((clock) => {
  // ...
  if (clock.getElapsedTime() > 5) loop.stop(app);
});
 
loop.start(app);

This loop will be destroyed after 5 seconds.

> GroupsGenerated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:46 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

Groups

Sometimes you need to make groups of objects (it’s not conveniently to apply transforms to each object when can make just one to a group). In Three.js you make it using THREE.Object3D and it’s children.

In Whitestorm.js we have WHS.Group that can do it in two ways:

Adding objects to an empty group

const sphere = new WHS.Sphere();
const box = new WHS.Box();
const group = new WHS.Group();
 
sphere.addTo(group);
box.addTo(group);

Making a group from objects

const sphere = new WHS.Sphere();
const box = new WHS.Box();
const group = new WHS.Group(box, sphere);
// OR: const group = new WHS.Group([box, sphere]);

You can list elements in sequence or pass an array. (see es6 rest/spread).

> 3D TransformsGenerated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:46 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

3D transforms

.position

Used in: MeshComponentLightComponentCameraComponent

position is a 3D vector (THREE.Vector3) object that defines where mesh is located in space.

ball.position.set(10, 20, 45);
 
ball.position.x // -> 10
ball.position.y // -> 20
ball.position.z // -> 45

Modifying values and methods

That’s several examples of how you can modify positon:

  • ball.position.set( x, y, z )
  • ball.position.setX( x ), (.setX().setY().setZ() methods).
  • ball.position = new THREE.Vector3( x, y, z )

There are a lot of other methods that .position handles as a THREE.Vector3. You may see the list of methods at Three.js documentation.

.rotation

Used in: MeshComponentLightComponentCameraComponent

rotation is a THREE.Euler with x, y and z values and has almost same methods as a .position. It defines a rotation regarding object position.

Modifying values and methods

Commonly used:

  • ball.rotation.set( x, y, z )
  • ball.rotation.setX( x ), (.setX().setY().setZ() methods).
  • ball.rotation = new THREE.Euler( x, y, z )

See list of THREE.Euler methods at Three.js documentation.

.rotation will be converted to a quaternion and applied to it’s physics object linked to the component. (Only if you use a Physics version [Todo: add link]).

.quaternion

Used in: MeshComponentLightComponentCameraComponent

quaternionTHREE.Quaternion is another way to rotate 3D object in space. It has x, y, z and w values.

// Convert euler to quaternion.
ball.quaternion.setFromEuler(new THREE.Euler(Math.PI / 2, 0, 0));
// Set values.
ball.quaternion.set(10, 20, 45, 60);
 
ball.quaternion.x // -> 10
ball.quaternion.y // -> 20
ball.quaternion.z // -> 45
ball.quaternion.w // -> 60

Modifying values and methods

Commonly used:

  • ball.quaternion.set( x, y, z, w )
  • ball.quaternion.setX( x ), (.setX().setY().setZ().setW() methods).
  • ball.quaternion = new THREE.Quaternion( x, y, z, w )
  • ball.quaternion.setFromEuler(new THREE.Euler( x, y, z ))

See list of THREE.Quaternion methods at Three.js documentation.

.scale

Used in: MeshComponent

scaleTHREE.Vector3 is a vector that defines mesh scale.

ball.scale.set(2, 2, 2)
 
ball.scale.x // -> 2
ball.scale.y // -> 2
ball.scale.z // -> 2

Modifying values and methods

Commonly used:

  • ball.scale.set( x, y, z )
  • ball.scale.setX( x ), (.setX().setY().setZ() methods).
  • ball.scale= new THREE.Vector3( x, y, z )

See list of THREE.Vector3 methods at Three.js documentation.

> Usage with webpackGenerated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:46 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

Usage with webpack

For better understanding see whitestorm-app-boilerplate

Using predefined alias

WhitestormJS provides alias configuration for all its source files to help you build awesome applications with webpack or other tools even faster.

import alias from 'whs/tools/alias';
 
export default {
  // ...
 
  resolve: {
    alias
  }
}

With just a few lines of setup – you can use our shorteners for src/ files:

  • @three – Three.js used in Whitestorm.js
  • @whs:app – App modules
  • @whs:app/element – Alias for specific app module (camerarendering, ..)
  • @whs:controls – Controls modules
  • @whs:controls/oribt – Alias for specific controls module (… firstperson)
  • @whs+meshes – Mesh components
  • @whs+meshes/Box@whs+meshes/Sphere, and so on….
  • @whs+lights – Light components
  • @whs+lights/AmbientLight@whs+lights/DirectionalLight, …

> ModulesGenerated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:46 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

Modules

Modules provide easy integration with components. They can make a lot of complex things much simpler to other developers (such as adding 3D physics).

Articles:

Simple module

Let’s create a simple module that will add .alertRandom() method to component, in which this module is used.

export default class SimpleModule {
  integrate() {
    this.alertRandom = function () {
      alert(Math.random());
    }
  }
}
import SimpleModule from './SimpleModule';
 
const sphere = new WHS.Sphere({
  modules: [
    new SimpleModule()
  ]
});
 
sphere.alertRandom(); // will alert a random number.

Advanced module

This example demonstrates the power of using modules.

export default class AdvancedModule {
  constructor(params = {}) {
    this.params = Object.assign({
      color: 0xff0000 // red
    }, params); // Polyfill params with default values
  }
 
  bridge = {
    material(inputMaterial, self) { // self - module scope
      const outputMaterial = inputMaterial.clone(); // You know, it's not required
 
      outputMaterial.color.setHex(self.params.color);
 
      return outputMaterial;
    }
  }
 
  integrate(self) { // self - module scope
    this.checkForColor = function () {
      if (this.material.color.getHex() === self.params.color) {
        alert("color is the same");
      } else {
        alert("???");
      }
    }
  }
 
  manager(manager) {
    manager.set('usedColor', this.params.color); // other modules can access this
  }
}
import {MeshComponent} from WHS;
import AdvancedModule from './AdvancedModule';
 
class MyComponent extends MeshComponent {
  build() {
    return new THREE.Mesh(
      new THREE.SphereGeometry(),
      this.applyBridge({material: new THREE.MeshBasicMaterial()}).material
    )
  }
}
 
const myInstance = new MyComponent({
  modules: [
    new AdvancedModule({color: 0x0000ff}) // blue color
  ]
});
 
myInstance.checkForColor(); // alerts "color is the same"

API

constructor()

Used to handle input parameters that define module’s behavior

.integrate(self)

In this method of any module, this is replaced with component’s instance. integrate() is executed once when component instance is created with new keyword.

self – module scope.

.postIntegrate(self)

Same as .integrate(self), but is invoked after .build(), that means you can use .defer(() => {}) inside it. Will be useful for working with models (WHS.Importer) and .native objects.

.manager(manager)

Allows components communicate with each other. manager – is a ModuleManager instance provided by Component or App, where module is used

.bridge

  • Object that handles functions called from component’s code with .applyBridge() method.
  • Used to overwrite some parts of component.
  • Functions will be called only if specific component provides API for this “bridge” (using .applyBridge({bridgeName: inputData}).bridgeName).

Module LifeCycle

Module methods are executed in this order:

  1. constructor()
  2. if ModuleManager is provided by component & .manager() exists -> .manager(manager)
  3. if .integrate() exists -> .integrate()
  4. bridges are executed if such API is provided by components.

Only constructor() is executed once when you create a module instance. All other methods are executed once per component, where they are used.

Therefore, you can use one module instance per multiple components in performance reasons (Recommended)

Bridges provided by included components (WHS.Sphere, …)

Bridge name Value provided
geometry THREE.Geometry instance
material THREE.Material instance
mesh THREE.Mesh instance

> Animation ClipsGenerated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:46 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

Animation Clips

The Animation Module provides a convenient approach to animate your models. Three.js provides an animation system, but you would need to setup the animation mixer, actions, and clips for your models. With the AnimationModule, a few lines of code is enough to get going with playing various loops your models might have.

Pipeline

A simple pipeline with Blender to get animated models on the browser:

Blender

  • Model your mesh

Modeling your mesh first, here is the model used in the Alien animation example.

  • Add bones

Then add bones to form the armature of the model for animation.

  • Weight assign (Skin/Rig)

Assign weight to vertices for the bones to influence.

  • Add animation frames

Create frames to start animating, here using animation frames. Use the dope sheet/actions editor.

  • Animation names

Your model might have multiple animations (actions/clips), here is the given name for the single animation created for this model.

  • Export

Finally, export to three.js json format, use the Blender exporter plugin, and ticket the appropriate options.

Whitestorm

  • Create the animation module, passing your app as parameter.
const animationModule = new AnimationModule(app);
  • Import your model as a SkinnedMesh, then play your clip
new Importer({
  parser(geometry, materials) {
    return new THREE.SkinnedMesh(geometry, materials);
  },
 
  url: `path/to/model.json`,
  useCustomMaterial: true,
 
  material: new THREE.MeshStandardMaterial({
    skinning: true
  }),
 
  modules: [animationModule]
}).addTo(app).then(() => {
  animationModule.play('clipName');
});

That’s it. The clip will kick off after the model is loaded, playing the given clip name.Generated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:46 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

core

Classes

App

CameraComponent

Component

LightComponent

Loop

MeshComponent

ModuleManager

ModuleSystemGenerated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:47 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

core/ App

new App(modulesopt)

This component is used to prepare a world scene, setup physics, camera, renderer and all other things that you usually do before making meshes.

Source:

Parameters:
Name Type Attributes Default Description
modules Array <optional> [] Array of Modules

Extends ModuleSystem

Members

.loops :Array

Array of loops that are executed by this app.

Source:

core/ CameraComponent

new CameraComponent(paramsopt, instructionsopt)

Source:

Parameters:
Name Type Attributes Description
params Object <optional> The parameters object.
instructions Object <optional> The instructions object.

Extends module:core.Component

Members

(private) ._wait :Array

Array of promises that should be resolved before Component is ready.

Inherited From:

Source:

.children :Array

Collection of child Components.

Inherited From:

Source:

.defaults :Object

Default values for parameters

Overrides:

Default Value:

              {
  build: true,
 
  position: {x: 0, y: 0, z: 0},
  rotation: {x: 0, y: 0, z: 0}
}
            

Source:

.instructions :Object

Static instructions

Overrides:

Default Value:

              {
  position: ['x', 'y', 'z'],
  rotation: ['x', 'y', 'z'],
  scale: ['x', 'y', 'z']
}
            

Source:

.isDeffered :Boolean

Returns whether the object is async (wait promises are more than 0).

Inherited From:

Source:

.manager :ModuleManager

Returns the ModuleManager used for this component.

Inherited From:

Source:

.modules :Array

Collection of modules.

Inherited From:

Source:

.native :Object

Returns the native object used for this component.

Inherited From:

Source:

Methods

.add(object) → {Promise}

Add a child Component.

Inherited From:

Source:

Parameters:
Name Type Description
object Component Component that should be added as a child.

.addTo(object)

Adds this Component to specified App/Component.

Inherited From:

Source:

Parameters:
Name Type Description
object Component Component that will be a parent of this.

.build()

Build livecycle should return a native object.

Source:

Throws:

CompositionError

.clone() → {CameraComponent}

Make a clone of this CameraComponent using .copy()

Overrides:

Source:

.copy() → {this}

Copy source transforms & execute Component.copy()

Overrides:

Source:

.defer(func)

Execute func (Callback) when Component is ready.

Inherited From:

Source:

Parameters:
Name Type Description
func function Callback.

.remove(object)

Remove a child Component.

Inherited From:

Source:

Parameters:
Name Type Description
object Component Component that should be a child of this Component.

.updateParams() → {Object}

Updates parameters of the Component.

Inherited From:

Source:

.wait(promiseopt) → {Promise}

Wait for a promise.

Inherited From:

Source:

Parameters:
Name Type Attributes Description
promise Promise <optional> The promise that should be added to a queue.

.wrap() → {Promise}

Wraps transforms (position & rotation)

Source:

Generated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:47 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.

core/ Component

new Component(paramsopt, instructionsopt)

Source:

Parameters:
Name Type Attributes Description
params Object <optional> The parameters object.
instructions Object <optional> The instructions object.

Extends ModuleSystem

Members

(private) ._wait :Array

Array of promises that should be resolved before Component is ready.

Source:

.children :Array

Collection of child Components.

Source:

.defaults :Object

Default values for parameters

Default Value:

              {
  modules: [],
  manager: true
}
            

Source:

.instructions :Object

Static instructions

Default Value:

              {}
            

Source:

.isDeffered :Boolean

Returns whether the object is async (wait promises are more than 0).

Source:

.manager :ModuleManager

Returns the ModuleManager used for this component.

Source:

.modules :Array

Collection of modules.

Source:

.native :Object

Returns the native object used for this component.

Source:

Methods

.add(object) → {Promise}

Add a child Component.

Source:

Parameters:
Name Type Description
object Component Component that should be added as a child.

.addTo(object)

Adds this Component to specified App/Component.

Source:

Parameters:
Name Type Description
object Component Component that will be a parent of this.

.clone() → {object}

Clone this component

Source:

.copy(source, customizeopt) → {this}

Copy source native and integrate modules to it.

Source: caça niquel

Parameters:
Name Type Attributes Description
source Component Source component that is used for copy() action.
customize function <optional> Callback executed before modules integration process.

.defer(func)

Execute func (Callback) when Component is ready.

Source: slot online

Parameters:
Name Type Description
func function Callback.

.remove(object)

Remove a child Component.

Source: uudet nettikasinot

Parameters:
Name Type Description
object Component Component that should be a child of this Component.

.updateParams() → {Object}

Updates parameters of the Component.

Source: Bestes Online Casino

.wait(promiseopt) → {Promise}

Wait for a promise.

Source:

Parameters:
Name Type Attributes Description
promise Promise <optional> The promise that should be added to a queue.

Generated by JSDoc 3.4.3 on Tue Jul 18 2020 14:29:47 GMT+0300 (EEST). Inspired by stack.gl
MIT, see LICENSE.md for details.