Note: This documentation is for the old 0.3.0 version of A-Frame. Check out the documentation for the current 1.5.0 version

The material component gives appearance to an entity. We can define properties such as color, opacity, or texture. This is often paired with the geometry component which provides shape.

Custom materials and shaders can be registered to extend the material component in order to provide a wide range of visual effects.


Defining a red material using the default standard material:

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

Here is an example of using a different material:

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

Here is an example of using an example custom material:

<a-entity geometry="primitive: plane"
material="shader: ocean; color: blue; wave-height: 10"></a-entity>


The material component has a few base properties. More properties will be available depending on the material applied.

Property Description Default Value
depthTest Whether depth testing is enabled when rendering the material. true
flatShading Use THREE.FlatShading rather than THREE.StandardShading. true
opacity Extent of transparency. If the transparent property is not true, then the material will remain opaque and opacity will only affect color. 1.0
transparent Whether material is transparent. Transparent entities are rendered after non-transparent entities. false
shader Which material to use. Defaults to the standard material. Can be set to the flat material or to a registered custom material. standard
side Which sides of the mesh to render. Can be one of front, back, or double. front
visible Whether material is visible. Raycasters will ignore invisible materials. true


Event Name Description
materialtextureloaded Texture loaded onto material. Or when the first frame is playing for video textures.
materialvideoended For video textures, emitted when the video has reached its end (may not work with loop).

Built-in Materials

A-Frame ships with a few built-in materials.


The standard material is the default material. It uses the physically-based THREE.MeshStandardMaterial.


These properties are available on top of the base material properties.

Property Description Default Value
color Base diffuse color. #fff
height Height of video (in pixels), if defining a video texture. 360
envMap Environment cubemap texture for reflections. Can be a selector to or a comma-separated list of URLs. None
fog Whether or not material is affected by fog. true
metalness How metallic the material is from 0 to 1. 0.5
repeat How many times a texture (defined by src) repeats in the X and Y direction. 1 1
roughness How rough the material is from 0 to 1. A rougher material will scatter reflected light in more directions than a smooth material. 0.5
width Width of video (in pixels), if defining a video texture. 640
src Image or video texture map. Can either be a selector to an <img> or <video>, or an inline URL. None

Physically-Based Shading

Physically-based shading is a shading model that aims to make materials behave realistically to lighting conditions. Appearance is a result of the interaction between the incoming light and the properties of the material.

To achieve realism, the diffuse color (can be supplied through the base material component), metalness, roughness properties of the material must be accurately controlled, often based on real-world material studies. Some people have compiled charts of realistic values for different kinds of materials that can be used as a starting point.

For example, for a tree bark material, as an estimation, we might set:

<a-entity geometry="primitive: cylinder"
material="src: treebark.png; color: #696969; roughness: 1; metalness: 0">

Environment Maps

The envMap property defines what environment the material reflects. Unlike textures, the envMap property takes a cubemap, six images put together to form a cube. The cubemap is wrapped around the mesh and applied as a texture. The clarity of the environment depends on the metalness, and roughness properties.

For example:

<a-cubemap id="sky">
<img src="right.png">
<img src="left.png">
<img src="top.png">
<img src="bottom.png">
<img src="front.png">
<img src="back.png">

<a-entity geometry="primitive: box" material="envMap: #sky; roughness: 0"></a-entity>


The flat material uses the THREE.MeshBasicMaterial. Flat materials are not affected by the scene’s lighting conditions. This is useful for things such as images or videos. Set shader to flat:

<a-entity geometry="primitive: plane" material="shader: flat; src: #cat-image"></a-entity>


Property Description Default Value
color Base diffuse color. #fff
fog Whether or not material is affected by fog. true
height Height of video (in pixels), if defining a video texture. 360
repeat How many times a texture (defined by src) repeats in the X and Y direction. 1 1
src Image or video texture map. Can either be a selector to an <img> or <video>, or an inline URL. None
width Width of video (in pixels), if defining a video texture. 640


To set a texture using one of the built-in materials, specify the src property. src can be a selector to either an <img> or <video> element in the asset management system:

<img id="my-texture" src="texture.png">

<a-entity geometry="primitive: box" material="src: #my-texture"></a-entity>

src can also be an inline URL. Note that we do not get browser caching or preloading through this method.

<a-entity geometry="primitive: box" material="src: url(texture.png)"></a-entity>

Most of the other properties works together with textures. For example, the color property will act as the base color and be multiplied per pixel with the texture. Set it to #fff to maintain the original colors of the texture.

Textures are cached by A-Frame in order to not push redundant textures to the GPU.

Video Textures

Whether the video texture loops or autoplays depends on the video element used to create the texture. If we simply pass a URL instead of creating and passing a video element, then the texture will loop and autoplay by default. To specify otherwise, create a video element in the asset management system, and pass a selector for the id attribute (e.g., #my-video):

<!-- No loop. -->
<video id="my-video" src="video.mp4" autoplay="true">

<a-entity geometry="primitive: box" material="src: #my-video"></a-entity>

Controlling Video Textures

To control the video playback such as pausing or seeking, we can use the video element to control media playback. For example:

var videoEl = document.querySelector('#my-video');
videoEl.currentTime = 122; // Seek to 122 seconds.

This doesn’t work as well if you are passing an inline URL, in which case a video element will be created internally. To get a handle on the video element, we should define one in <a-assets>.

Canvas Textures

We can use a <canvas> as a texture source. The texture will automatically refresh itself as the canvas changes.

AFRAME.registerComponent('draw-canvas', {
schema: {default: ''},

init: function () {
this.canvas = document.getElementById(;
this.ctx = this.canvas.getContext('2d');

// Draw on canvas...

<canvas id="my-canvas" crossorigin="anonymous"></canvas>

<a-entity geometry="primitive: plane" material="src: #my-canvas"

Repeating Textures

We might want to tile textures rather than having them stretch. The repeat property can be used to repeat textures.

<a-entity geometry="primitive: plane; width: 100"
material="src: carpet.png; repeat: 100 20"></a-entity>

Transparency Issues

Transparency and alpha channels are tricky in 3D graphics. If you are having issues where transparent materials in the foreground do not composite correctly over materials in the background, it is probably due to underlying design of the OpenGL compositor (which WebGL is an API for).

In an ideal scenario, transparency in A-Frame would “just work”, regardless of where the developer places an entity in 3D space, or in which order they define the elements in markup. In the current version of A-Frame, however, it is easy to create scenarios where foreground entities occlude background entities. This creates confusion and unwanted visual defects.

To work around, try changing the order of the entities.

Register a Custom Material

We can register custom materials for appearances and effects using AFRAME.registerShader.


Like components, custom materials have schema and lifecycle handlers.

Property Description
schema Defines properties, uniforms, attributes that the shader will use to extend the material component.
init Lifecycle handler called once during shader initialization. Used to create the material.
update Lifecycle handler called once during shader initialization and when data is updated. Used to update the material or shader.


We can define material properties just as we would with component properties. The data will act as the data we use to create our material:

AFRAME.registerShader('custom', {
schema: {
emissive: {default: '#000'},
wireframe: {default: false}


To create a custom material, we have the init and update handlers set and update this.material to the desired material. Here is an example of registering a THREE.LinedDashedMaterial:

AFRAME.registerShader('line-dashed', {
schema: {
dashSize: {default: 3},
lineWidth: {default: 1}

* `init` used to initialize material. Called once.
init: function (data) {
this.material = new THREE.LineDashedMaterial(data);
this.update(data); // `update()` currently not called after `init`. (#1834)

* `update` used to update the material. Called on initialization and when data updates.
update: function (data) {
this.material.dashsize = data.dashsize;
this.material.linewidth = data.linewidth;

Register a Custom GLSL Shader

We also use registerShader for registering THREE.ShaderMaterials to create custom shaders.

We can provide our own GLSL vertex and fragment shaders (small programs that run on the GPU), and we can define a schema for their uniforms and attributes just as we would with component schemas. The shader’s schema will extend the base material component’s schema, and as a result we can pass values from HTML directly to the shader.


THREE.ShaderMaterial-based schemas pass uniforms to shaders. To specify a uniform, set is to uniform:

AFRAME.registerShader('sun', {
schema: {
sunPosition: {type: 'vec3', is: 'uniform'},
time: {type: 'time', is: 'uniform'}

Property Types

These property types will be converted to the appropriate GLSL types.

Type Description
color Built-in convenience (vec3) uniform type. Will take colors in multiple formats and automatically convert them to vec3 format (e.g., ‘red’ -> THREE.Vector3(1, 0, 0))
number Maps to GLSL float.
time Built-in convenience (float) uniform type. If specified, the material component will continuously update the shader with the global scene time.
vec2 Maps to GLSL vec2.
vec3 Maps to GLSL vec3.
vec4 Maps to GLSL vec4.


Here is a simple shader that sets the material to a flat color. The vertex shader shown is the default vertex shader. The shaders need to be a string:

AFRAME.registerShader('hello-world', {
schema: {
color: {type: 'vec3', default: '0.5 0.5 0.5', is: 'uniform'}

vertexShader: [
'void main() {',
' gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',

fragmentShader: [
'uniform vec3 color;'
'void main() {'
' gl_FragColor = vec4(color, 1.0);',

Then to use the custom shader, we set the material component’s shader property to the name of the registered shader. Then we pass the defined shader uniforms as properties like we would with components:

<a-entity geometry="primitive: box"
material="shader: hello-world; color: 0.3 0.3 0.3"></a-entity>

Additional Resources