A-Frame Component Lifecycle: init, update, and tick
Decide where per-frame logic belongs in A-Frame components, with a spinner example and a-assets preloading.
17 Aug 2026, 14:25 UTC

You add a rotating object to an A-Frame scene, put the rotation code in the component’s init method, and notice it runs exactly once. Or you write a requestAnimationFrame loop outside the component and wonder why the inspector shows stale component state. The scene works, but the animation feels disconnected from the A-Frame entity system.
The useful takeaway is to match the work to the lifecycle method: init for one-time setup, update for reactions to changed attributes, and tick for per-frame motion. Also preload models and textures in a-assets so the first render does not pop in. An entity is the scene object (for example a-entity or a primitive such as a-box); a component is reusable behavior registered with AFRAME.registerComponent.
The three lifecycle jobs
A-Frame calls component methods at predictable times. Use this table to decide where code belongs.
| Method | When it runs | Best for |
|---|---|---|
init |
Once, when the component attaches to an entity | One-time setup, attaching event listeners |
update(oldData) |
At initialization and whenever component data changes | Reacting to attribute changes |
tick(time, delta) |
Every animation frame for each component that defines it | Movement, rotation, intersection checks |
tick receives time (total elapsed milliseconds) and delta (milliseconds since the previous frame). Multiplying a rotation or speed by delta / 1000 keeps motion frame-rate independent.
Worked example: a frame-rate-independent spinner
Save this as an HTML file, load A-Frame from your project’s pinned release URL, and open it in a desktop browser. This example is a pattern, not a tested snippet.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A-Frame lifecycle example</title>
<!-- Insert your pinned A-Frame release URL here -->
<script src="YOUR_AFRAME_RELEASE_URL"></script>
</head>
<body>
<a-scene>
<a-assets>
<a-asset-item id="box-model" src="./models/box.glb"></a-asset-item>
</a-assets>
<a-entity
id="spinner"
geometry="primitive: box"
material="color: crimson"
spinner="speed: 90"
position="0 1 -5">
</a-entity>
</a-scene>
<script>
AFRAME.registerComponent('spinner', {
schema: { speed: { default: 45, type: 'number' } },
init: function () {
console.log('spinner init');
},
update: function (oldData) {
console.log('spinner update', oldData);
},
tick: function (time, delta) {
const el = this.el;
el.object3D.rotation.y += (this.data.speed * Math.PI / 180) * (delta / 1000);
}
});
</script>
</body>
</html>
Where to run: any local web server or directly as a file, though external assets may be blocked by CORS when opening file://. Permissions: none beyond normal browser use. Placeholders: replace YOUR_AFRAME_RELEASE_URL and ./models/box.glb with your project’s values.
Expected checks: open DevTools Console and refresh. You should see spinner init once. Change the attribute at runtime, for example by selecting the entity in the A-Frame inspector or running document.querySelector('#spinner').setAttribute('spinner', 'speed', 120) in the console, and spinner update should fire. Do not log inside tick; it runs every frame and will flood the console.
Risks: logging inside tick degrades frame rate. Many entities with tick increase per-frame cost, especially on standalone headsets.
Preload assets with a-assets
Place images, models, and textures inside a-assets. The scene waits for listed assets before the first render, which avoids visible pop-in. The timeout attribute on a-asset-item prevents one slow asset from blocking startup indefinitely.
<a-assets>
<img id="floor-texture" src="./textures/floor.jpg">
<a-asset-item id="character-model" src="./models/character.glb" timeout="5000"></a-asset-item>
</a-assets>
Reference them elsewhere in the scene by ID, such as material="src: #floor-texture" or gltf-model="#character-model".
Trade-off: ticking is not free
Every component that defines tick runs on every frame. A scene with dozens of ticking entities can lower frame rate on standalone headsets, which breaks comfort and can cause motion sickness. Prefer event-driven updates: let a click or mouseenter handler change component data, and let update react. Inside tick, guard expensive work with a time check or a flag so it does not run every frame when idle.
Practical verification: add stats to a-scene to show FPS, or record the page in Chrome DevTools Performance. Open the A-Frame inspector with Ctrl+Alt+I in a desktop browser to confirm attribute changes flow through update and that component state matches the DOM. Measure on the target headset, not only a desktop; there are no universal performance numbers for ticking components.
Closing action
Audit your custom components. Move one-time setup to init, attribute reactions to update, and continuous motion to tick using delta. Preload scene assets in a-assets. Then verify frame pacing on the actual device you plan to support.
Lifecycle method names have been stable across the A-Frame 1.x line, but confirm the exact signatures against your project’s pinned release notes before relying on them.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.