aframe

Topics related to aframe:

Getting started with aframe

This section provides an overview of what aframe is, and why a developer might want to use it.

It should also mention any large subjects within aframe, and link out to the related topics. Since the Documentation for aframe is new, you may need to create initial versions of those related topics.

Raycasters (component)

Events

NameDetails
raycaster-intersectedEmitted on the intersected entity. Entity is intersecting with a raycaster. Event detail will contain el, the raycasting entity, and intersection, an object containing detailed data about the intersection.
raycaster-intersected-clearedEmitted on the intersected entity. Entity is no longer intersecting with a raycaster. Event detail will contain el, the raycasting entity.
raycaster-intersectionEmitted on the raycasting entity. Raycaster is intersecting with one or more entities. Event detail will contain els, an array with the intersected entities, and intersections, an array of objects containing detailed data about the intersections.
raycaster-intersection-clearedEmitted on the raycasting entity. Raycaster is no longer intersecting with an entity. Event detail will contain el, the formerly intersected entity.

Member

MemberDescription
intersectedElsEntities currently intersecting the raycaster.
objectsthree.js objects to test for intersections. Will be scene.children if not objects property is not specified.
raycasterthree.js raycaster object.

Methode

MethodDescription
refreshObjectsRefreshes the list of objects based off of the objects property to test for intersection.

Entities <a-entity>

METHODS


addState (stateName)

addState will push a state onto the entity. This will emit the stateadded event, and we can check the state can for existence using .is:

entity.addEventListener('stateadded', function (evt) {
  if (evt.detail.state === 'selected') {
    console.log('Entity now selected!');
  }
});
entity.addState('selected');
entity.is('selected');  // >> true

emit (name, detail, bubbles)

emit emits a custom DOM event on the entity. For example, we can emit an event to trigger an animation:

<a-entity>
  <a-animation attribute="rotation" begin="rotate" to="0 360 0"></a-animation>
</a-entity>
entity.emit('rotate');

We can also pass event detail or data as the second argument:

entity.emit('collide', { target: collidingEntity });

The event will bubble by default. we can tell it not to bubble by passing false for bubble:

entity.emit('sink', null, false);

flushToDOM (recursive)

flushToDOM will manually serialize an entity’s components’ data and update the DOM.


getAttribute (componentName)

getAttribute retrieves parsed component data (including mixins and defaults).

// <a-entity geometry="primitive: box; width: 3">
entity.getAttribute('geometry');
// >> {primitive: "box", depth: 2, height: 2, translate: "0 0 0", width: 3, ...}
entity.getAttribute('geometry').primitive;
// >> "box"
entity.getAttribute('geometry').height;
// >> 2
entity.getAttribute('position');
// >> {x: 0, y: 0, z: 0}

If componentName is not the name of a registered component, getAttribute will behave as it normally would:

// <a-entity data-position="0 1 1">
entity.getAttribute('data-position');
// >> "0 1 1"

getDOMAttribute (componentName)

getDOMAttribute retrieves only parsed component data that is explicitly defined in the DOM or via setAttribute. If componentName is the name of a registered component, getDOMAttribute will return only the component data defined in the HTML as a parsed object. getDOMAttribute for components is the partial form of getAttribute since the returned component data does not include applied mixins or default values:

Compare the output of the above example of getAttribute:

// <a-entity geometry="primitive: box; width: 3">
entity.getDOMAttribute('geometry');
// >> { primitive: "box", width: 3 }
entity.getDOMAttribute('geometry').primitive;
// >> "box"
entity.getDOMAttribute('geometry').height;
// >> undefined
entity.getDOMAttribute('position');
// >> undefined

getObject3D (type)

getObject3D looks up a child THREE.Object3D referenced by type on object3DMap.

AFRAME.registerComponent('example-mesh', {
  init: function () {
     var el = this.el;
     el.getOrCreateObject3D('mesh', THREE.Mesh);
     el.getObject3D('mesh');  // Returns THREE.Mesh that was just created.
  }
});

getOrCreateObject3D (type, Constructor)

If the entity does not have a THREE.Object3D registered under type, getOrCreateObject3D will register an instantiated THREE.Object3D using the passed Constructor. If the entity does have an THREE.Object3D registered under type, getOrCreateObject3D will act as getObject3D:

AFRAME.registerComponent('example-geometry', {
  update: function () {
    var mesh = this.el.getOrCreateObject3D('mesh', THREE.Mesh);
    mesh.geometry = new THREE.Geometry();
  }
});

pause ()

pause() will stop any dynamic behavior as defined by animations and components. When we pause an entity, it will stop its animations and call Component.pause() on each of its components. The components decide to implement what happens on pause, which is often removing event listeners. An entity will call pause() on its child entities when we pause an entity.

// <a-entity id="spinning-jumping-ball">
entity.pause();

For example, the look-controls component on pause will remove event handlers that listen for input.


play ()

play() will start any dynamic behavior as defined by animations and components. This is automatically called when the DOM attaches an entity. When an entity play(), the entity calls play() on its child entities.

entity.pause();
entity.play();

For example, the sound component on play will begin playing the sound.


setAttribute (componentName, value, [propertyValue | clobber])

If componentName is not the name of a registered component or the component is a single-property component, setAttribute behaves as it normally would:

entity.setAttribute('visible', false);

Though if componentName is the name of a registered component, it may handle special parsing for the value. For example, the position component is a single-property component, but its property type parser allows it to take an object:

entity.setAttribute('position', { x: 1, y: 2, z: 3 });

setObject3D (type, obj)

setObject3D will register the passed obj, a THREE.Object3D, as type under the entity’s object3DMap. A-Frame adds obj as a child of the entity’s root object3D.

AFRAME.registerComponent('example-orthogonal-camera', {
  update: function () {
    this.el.setObject3D('camera', new THREE.OrthogonalCamera());
  }
});

removeAttribute (componentName, propertyName)

If componentName is the name of a registered component, along with removing the attribute from the DOM, removeAttribute will also detach the component from the entity, invoking the component’s remove lifecycle method.

entity.removeAttribute('goemetry');  // Detach the geometry component.
entity.removeAttribute('sound');  // Detach the sound component.

If propertyName is given, removeAttribute will reset the property value of that property specified by propertyName to the property’s default value:

entity.setAttribute('material', 'color', 'blue');  // The color is blue.
entity.removeAttribute('material', 'color');  // Reset the color to the default value, white.

removeObject3D (type)

removeObject3D removes the object specified by type from the entity’s THREE.Group and thus from the scene. This will update the entity’s object3DMap, setting the value of the type key to null. This is generally called from a component, often within the remove handler:

AFRAME.registerComponent('example-light', {
  update: function () {
    this.el.setObject3D('light', new THREE.Light());
    // Light is now part of the scene.
    // object3DMap.light is now a THREE.Light() object.
  },
  remove: function () {
    this.el.removeObject3D('light');
    // Light is now removed from the scene.
    // object3DMap.light is now null.
  }
});

removeState (stateName)

removeState will pop a state from the entity. This will emit the stateremoved event, and we can check the state its removal using .is:

entity.addEventListener('stateremoved', function (evt) {
  if (evt.detail.state === 'selected') {
    console.log('Entity no longer selected.');
  }
});
entity.addState('selected');
entity.is('selected');  // >> true
entity.removeState('selected');
entity.is('selected');  // >> false

EVENTS

Event NameDescription
child-attachedA child entity was attached to the entity.
child-detachedA child entity was detached from the entity.
componentchangedOne of the entity’s components was modified.
componentinitOne of the entity’s components was initialized.
componentremovedOne of the entity’s components was removed.
loadedThe entity has attached and initialized its components.
object3dsetTHREE.Object3D was set on entity using setObject3D(name). Event detail will contain name used to set on the object3DMap.
pauseThe entity is now inactive and paused in terms of dynamic behavior.
playThe entity is now active and playing in terms of dynamic behavior.
stateaddedThe entity received a new state.
stateremovedThe entity no longer has a certain state.
schemachangedThe schema of a component was changed.

EVENT DETAILS

Below is what the event detail contains for each event:

Event NamePropertyDescription
child-attachedelReference to the attached child element.
componentchangednameName of component that had its data modified.
idID of component that had its data modified.
newDataComponent’s new data, after it was modified.
oldDataComponent’s previous data, before it was modified.
componentinitializednameName of component that was initialized.
idID of component that had its data modified.
dataComponent data.
componentremovednameName of component that was removed.
idID of component that was removed.
stateaddedstateThe state that was attached (string).
stateremovedstateThe state that was detached (string).
schemachangedcomponentName of component that had it’s schema changed.

System

METHODS

A system, like a component, defines lifecycle handlers. It can also define methods intended to be public API.

MethodDescription
initCalled once when the system is initialized. Used to initialize.
pauseCalled when the scene pauses. Used to stop dynamic behavior.
playCalled when the scene starts or resumes. Used to start dynamic behavior.
tickIf defined, will be called on every tick of the scene's render loop.

Components

Definition Lifecycle Handler Methods

With the schema being the anatomy, the lifecycle methods are the physiology; the schema defines the shape of the data, the lifecycle handler methods use the data to modify the entity. The handlers will usually interact with the Entity API.

Overview of Methods

MethodDescription
initCalled once when the component is initialized. Used to set up initial state and instantiate variables.
updateCalled both when the component is initialized and whenever any of the component's properties is updated (e.g, via setAttribute). Used to modify the entity.
removeCalled when the component is removed from the entity (e.g., via removeAttribute) or when the entity is detached from the scene. Used to undo all previous modifications to the entity.
tickCalled on each render loop or tick of the scene. Used for continuous changes or checks.
playCalled whenever the scene or entity plays to add any background or dynamic behavior. Also called once when the component is initialized. Used to start or resume behavior.
pauseCalled whenever the scene or entity pauses to remove any background or dynamic behavior. Also called when the component is removed from the entity or when the entity is detached from the scene. Used to pause behavior.
updateSchemaCalled whenever any of the component's properties is updated. Can be used to dynamically modify the schema.

Component Prototype Properties

Within the methods, we have access to the component prototype via this:

PropertyDescription
this.dataParsed component properties computed from the schema default values, mixins, and the entity's attributes.
this.elReference to the [entity][entity] as an HTML element.
this.el.sceneElReference to the [scene][scene] as an HTML element.
this.idIf the component can have [multiple instances][multiple], the ID of the individual instance of the component (e.g., foo from sound__foo).

METHODS

.init ()

.init () is called once at the beginning of the component's lifecycle. An entity can call the component's init handler:

  • When the component is statically set on the entity in the HTML file and the page is loaded.
  • When the component is set on an attached entity via setAttribute.
  • When the component is set on an unattached entity, and the entity is then attached to the scene via appendChild.

The init handler is often used to:

  • Set up initial state and variables
  • Bind methods
  • Attach event listeners

For example, a cursor component's init would set state variables, bind methods, and add event listeners:

AFRAME.registerComponent('cursor', {
  // ...
  init: function () {
    // Set up initial state and variables.
    this.intersection = null;
    // Bind methods.
    this.onIntersection = AFRAME.utils.bind(this.onIntersection, this);
    // Attach event listener.
    this.el.addEventListener('raycaster-intersection', this.onIntersection);
  }
  // ...
});

.update (oldData)

.update (oldData) is called whenever the component's properties change, including at the beginning of the component's lifecycle. An entity can call a component's update handler:

  • After init () is called, at the beginning of component's lifecycle.
  • When the component's properties are updated with .setAttribute.

The update handler is often used to:

  • Do most of the work in making modifications to the entity, using this.data.
  • Modify the entity whenever one or more component properties change.

Granular modifications to the entity can be done by [diffing][diff] the current dataset (this.data) with the previous dataset before the update (oldData).

A-Frame calls .update() both at the beginning of a component's lifecycle and every time a component's data changes (e.g., as a result of setAttribute). The update handler often uses this.data to modify the entity. The update handler has access to the previous state of a component's data via its first argument. We can use the previous data of a component to tell exactly which properties changed to do granular updates.

For example, the visible component's update sets the visibility of the entity.

AFRAME.registerComponent('visible', {
  /**
   * this.el is the entity element.
   * this.el.object3D is the three.js object of the entity.
   * this.data is the component's property or properties.
   */
  update: function (oldData) {
    this.el.object3D.visible = this.data;
  }
  // ...
});

.remove ()

.remove () is called whenever the component is detached from the entity. An entity can call a component's remove handler:

  • When the component is removed from the entity via removeAttribute.
  • When the entity is detached from the scene (e.g., removeChild).

The remove handler is often used to:

  • Remove, undo, or clean up all of the component's modifications to the entity.
  • Detach event listeners.

For example, when the [light component][light] is removed, the light component will remove the light object that it had previously set on the entity, thus removing it from the scene.

AFRAME.registerComponent('light', {
  // ...
  remove: function () {
    this.el.removeObject3D('light');
  }
  // ...
});

.tick (time, timeDelta)

.tick () is called on each tick or frame of the scene's render loop. The scene will call a component's tick handler:

  • On each frame of the render loop.
  • On the order of 60 to 120 times per second.
  • If the entity or scene is not paused (e.g., the Inspector is open).
  • If the entity is still attached to the scene.

The tick handler is often used to:

  • Continuously modify the entity on each frame or on an interval.
  • Poll for conditions.

The tick handler is provided the global uptime of the scene in milliseconds (time) and the time difference in milliseconds since the last frame (timeDelta). These can be used for interpolation or to only run parts of the tick handler on a set interval.

For example, the tracked controls component will progress the controller's animations, update the controller's position and rotation, and check for button presses.

AFRAME.registerComponent('tracked-controls', {
  // ...
  tick: function (time, timeDelta) {
    this.updateMeshAnimation();
    this.updatePose();
    this.updateButtons();
  }
  // ...
});

.pause ()

.pause () is called when the entity or scene pauses. The entity can call a component's pause handler:

  • Before the component is removed, before the remove handler is called.
  • When the entity is paused with Entity.pause ().
  • When the scene is paused with Scene.pause () (e.g., the Inspector is opened).

The pause handler is often used to:

  • Remove event listeners.
  • Remove any chances of dynamic behavior.

For example, the sound component will pause the sound and remove an event listener that would have played a sound on an event:

AFRAME.registerComponent('sound', {
  // ...
  pause: function () {
    this.pauseSound();
    this.removeEventListener();
  }
  // ...
});

.play ()

.play () is called when the entity or scene resumes. The entity can call a component's play handler:

  • When the component is first attached, after the update handler is called.
  • When the entity was paused but then resumed with Entity.play ().
  • When the scene was paused but then resumed with Scene.play ().

The play handler is often use to:

  • Add event listeners.

For example, the sound component will play the sound and update the event listener that would play a sound on an event:

AFRAME.registerComponent('sound', {
  // ...
  play: function () {
    if (this.data.autoplay) { this.playSound(); }
    this.updateEventListener();
  }
  // ...
});

.updateSchema (data)

.updateSchema (), if defined, is called on every update in order to check if the schema needs to be dynamically modified.

The updateSchema handler is often used to:

  • Dynamically update or extend the schema, usually depending on the value of a property.

For example, the geometry component checks if the primitive property changed to determine whether to update the schema for a different type of geometry:

AFRAME.registerComponent('geometry', {
  // ...
  updateSchema: (newData) {
    if (newData.primitive !== this.data.primitive) {
      this.extendSchema(GEOMETRIES[newData.primitive].schema);
    }
  }
  // ...
});

COMPONENT PROTOTYPE METHODS

.flushToDOM ()

To save on CPU time on stringification, A-Frame will only update in debug mode the component’s serialized representation in the actual DOM. Calling flushToDOM () will manually serialize the component’s data and update the DOM:

document.querySelector('[geometry]').components.geometry.flushToDOM();

Scene <a-scene>

METHODS

NameDescription
enterVRSwitch to stereo render and push content to the headset. Needs to be called within a user-generated event handler like click. the first time a page enters VR.
exitVRSwitch to mono renderer and stops presenting content on the headset.
reloadRevert the scene to its original state.

EVENTS

NameDescription
enter-vrUser has entered VR and headset started presenting content.
exit-vrUser has exited VR and headset stopped presenting content.
loadedAll nodes have loaded.
renderstartRender loop has started.

Asset Management System

Events

Since <a-assets> and <a-asset-item> are nodes in A-Frame, they will emit the loaded event when they say they have finished loading.

Event NameDescription
loadedAll assets were loaded, or assets timed out.
timeoutAssets timed out.

Load Progress on Individual Assets

<a-asset-item>

<a-asset-item> invokes the three.js FileLoader. We can use <a-asset-item> for any file type. When finished, it will set its data member with the text response.

Event NameDescription
errorFetch error. Event detail contains xhr with XMLHttpRequest instance.
progressEmitted on progress. Event detail contains xhr with XMLHttpRequest instance, loadedBytes, and totalBytes.
loadedAsset pointed to by src was loaded.

<img>

Images are a standard DOM element so we can listen to the standard DOM events.

Event NameDescription
loadImage was loaded.

HTMLMediaElement

Audio and video assets are HTMLMediaElements. The browser triggers particular events on these elements; noted here for convenience:

Event NameDescription
errorThere was an error loading the asset.
loadeddataProgress.
progressProgress.

A-Frame uses these progress events, comparing how much time the browser buffered with the duration of the asset, to detect when the asset becomes loaded.

Animation <a-animation>

Attributes

Here is an overview of animation attributes. We'll go into more detail below.

AttributeDescriptionDefault Value
attributeAttribute to animate. To specify a component attribute, use componentName.property syntax (e.g., light.intensity).rotation
beginEvent name to wait on before beginning animation.''
delayDelay (in milliseconds) or event name to wait on before beginning animation.0
directionDirection of the animation (between from and to). One of alternate, alternateReverse, normal, reverse.normal
durDuration in (milliseconds) of the animation.1000
easingEasing function of the animation. There are very many to choose from.ease
endEvent name to wait on before stopping animation.''
fillDetermines effect of animation when not actively in play. One of backwards, both, forwards, none.forwards
fromStarting value.Current value.
repeatRepeat count or indefinite.0
toEnding value. Must be specified.None

Begin

The begin attribute defines when the animation should start playing.

This can either be a number, representing milliseconds to wait, or an event name to wait for. For example, we can define an animation that waits 2 seconds before scaling an entity.

<a-entity>
  <a-animation attribute="scale" begin="2000" to="2 2 2"></a-animation>
</a-entity>

Or we can define an animation that waits for the parent element to trigger an event named fade before fading an entity.

<a-entity id="fading-cube" geometry="primitive: box" material="opacity: 1">
  <a-animation attribute="material.opacity" begin="fade" to="0"></a-animation>
</a-entity>
// Trigger an event to begin fading.
document.querySelector('#fading-cube').emit('fade');

Direction

The direction attribute defines which way to animate between the starting value and the final value.

When we define an alternating direction, the animation will go back and forth between the from and to values like a yo-yo. Alternating directions only take affect when we repeat the animation.

ValueDescription
alternateOn even-numbered cycles, animate from from to to. On odd-numbered cycles, animation from to to from
alternate-reverseOn odd-numbered cycles, animate from from to to. On even-numbered cycles, animation from to to from
normalAnimate from from to to.
reverseAnimate from to to from.

Easing

The easing attribute defines the easing function of the animation, which defaults to ease. There are too many easing functions to list, but we can implicitly explain them.

One possible value is linear. And the basic easing functions are ease, ease-in, ease-out, and ease-in-out.

Then there are more groups of easing functions. The above basic easing functions prefix each group of easing functions. The groups of easing functions are cubic, quad, quart, quint, sine, expo, circ, elastic, back, and bounce.

For example, the cubic group of easing functions would consist of ease-cubic, ease-in-cubic, ease-out-cubic, ease-in-out-cubic.

Fill

The fill attribute defines the effect of animation when not actively in play. Think of fill as what values the animation sets on the entity before and/or after each animation cycle. Below are the possible values for fill and their effects.

ValueDescription
backwardsBefore the animation starts, set the starting value to the from value.
bothCombine the effects of both backwards fill and forwards fill.
forwardsAfter the animation finishes, the final value will stay at the to value. The default fill.
noneBefore the animation starts, set the starting value to the initial value. After the animation finishes, reset the value to the initial value.

Repeat

The repeat attribute defines how often the animation repeats. We call each repeat of the animation a cycle. Repeat can either be a number that counts down on each animation cycle until it reaches 0 at which point the animation will end, or we can set repeat to indefinite and the animation will loop continuously until the animation is manually removed or stopped.


EVENTS

The <a-animation> element emits a couple of events.

Event NameDescription
animationendEmitted when the animation finishes. In case of repeats, emitted when the repeat count reaches 0. Not emitted for indefinite repeats.
animationstartEmitted immediately when the animation begins playing.

Mixins <a-mixin>

blend-model (component)

VALUES

TypeDescription
selectorSelector to an <a-asset-item>
stringurl()-enclosed path to a JSON file

EVENTS

Event NameDescription
model-loadedJSON model was loaded into the scene.

Primitives

Under the Hood

Primitives act as a convenience layer (i.e., syntactic sugar) primarily for newcomers. Keep in mind for now that primitives are <a-entity>s under the hood that:

  • Have a semantic name (e.g., <a-box>)
  • Have a preset bundle of components with default values
  • Map or proxy HTML attributes to [component][component] data

Primitives are similar to prefabs in Unity. Some literature on the entity-component-system pattern refer to them as assemblages. They abstract the core entity-component API to:

  • Pre-compose useful components together with prescribed defaults
  • Act as a shorthand for complex-but-common types of entities (e.g., <a-sky>)
  • Provide a familiar interface for beginners since A-Frame takes HTML in a new direction

Under the hood, this <a-box> primitive:

<a-box color="red" width="3"></a-box>

represents this entity-component form:

<a-entity geometry="primitive: box; width: 3" material="color: red"></a-entity>

<a-box> defaults the geometry.primitive property to box. And the primitive maps the HTML width attribute to the underlying geometry.width property as well as the HTML color attribute to the underlying material.color property.

light (component)

Controls (component)

It's possible that you must enable gamepadextentions. You could do that using this steps:

  • On Chrome: browse to chrome://flags
  • On Firefox: browse to about:config
  • On IE: Go to Group Policy Editor on your desktop
  • On Opera: browse to opera:config
  • On Edge: browse to about:flags

cursors

The cursor is a specific application of the raycaster component in that it

  • Listens for mouse clicks and gaze-based fuses
  • Captures only the first intersected entity
  • Emits special mouse and hover events (e.g., relating to mouse down/up/enter/leave)
  • Has more states for hovering.

When the mouse clicks, the closest visible entity intersecting the cursor, if any, will emit a click event. Note the cursor component only applies the raycasting behavior. To provide a shape or appearance to the cursor, you could apply the geometry and material components.


Events

EventDescription
clickEmitted on both cursor and intersected entity if a currently intersected entity is clicked (whether by mouse or by fuse).
fusingEmitted on both cursor and intersected entity when fuse-based cursor starts counting down.
mousedownEmitted on both cursor and intersected entity (if any) on mousedown on the canvas element.
mouseenterEmitted on both cursor and intersected entity (if any) when cursor intersects with an entity.
mouseleaveEmitted on both cursor and intersected entity (if any) when cursor no longer intersects with previously intersected entity.
mouseupEmitted on both cursor and intersected entity (if any) on mouseup on the canvas element.

Camera

When not in VR mode, userHeight translates the camera up to approximate average height of human eye level. The injected camera has this set to 1.6 (meters). When entering VR, this height offset is removed such that we used absolute position returned from the VR headset. The offset is convenient for experiences that work both in and out of VR, as well as making experiences look decent from a desktop screen as opposed to clipping the ground if the headset was resting on the ground.

When exiting VR, the camera will restore its rotation to its rotation before it entered VR. This is so when we exit VR, the rotation of the camera is back to normal for a desktop screen.

gltf-model (component)