Building An Immersive Recreation with A-Frame And Low Poly Models

By | March 7, 2018

Note: That is Half 1 of a two-part tutorial.

There is an enormous distinction between immersion and realism. A high-end pc recreation with detailed fashions and a strong GPU can really feel real looking, however nonetheless not really feel immersive. Theres extra to creating a sense of being there than polygon rely. A low poly expertise can really feel very immersive by means of cautious set design and lighting selections, with out being real looking at all.

Today Im going to indicate you construct a easy however immersive recreation with A-Frame and fashions from the earlier Sketchfab design problem. In distinction to my earlier tutorials, on this one we’ll stroll by means of creating the entire application. Not simply the essential interplay, but in addition including and positioning 3d fashions, programmatically constructing a panorama with rocks, including sounds and lighting to make the participant really feel immersed within the setting, and at last interplay tweaks for various form factors.

I hope this weblog will encourage you to undergo the present problem we’re working with SketchFab. Theres nonetheless time to enter earlier than submissions shut on April 2nd.


Our WebVR Whack-an-Imp recreation is a variation on Whack-A-Mole, besides in our case it goes to be an imp flying out of a effervescent cauldron. Earlier than we get to fancy 3D fashions, nevertheless, we must begin with an empty HTML file that features the A-Frame library.

<html> <head> <!– aframe itself –> <script src=””></script> </head> <body> </body></html>

At first we wont make the scene fairly in any respect. We simply wish to show that our idea will work, so we’ll preserve it easy. Which means no lighting, fashions, or sound results. As quickly as the underlying idea is confirmed we’ll make it pretty.

Lets begin off with a scene with stats turned on, then add a digicam with look-controls at a top of 1.5 m; which is an effective digicam top for VR interplay (roughly equivalent to the typical eye top of most grownup humans).

<a-scene stats> <a-entity digicam look-controls position=”0 1.5 0″> <a-cursor></a-cursor> </a-entity></a-scene>

Notice the a-cursor within the digicam. This may draw a bit of round cursor, which is essential for shows that dont have controllers, corresponding to Cardboard.

Our recreation may have an object that pops up from a cauldron, then falls again down as gravity takes maintain. The participant may have a paddle or persist with hit the item. If the participant misses, then the item ought to fall on the bottom. For now lets characterize the item with a sphere and the bottom with a easy aircraft. Put this code within the a-scene.

<a-entity id=’ball’ position=”0 1 -4″ material=”color:green;” geometry=”primitive:sphere; radius: 0.5;”></a-entity> <a-plane color=’red’ rotation=”-90 0 0″ width=”100″ height=”100″></a-plane>

Note that Im utilizing the lengthy syntax of a-entity for the ball slightly than a-sphere. Thats as a outcome of later we’ll change the geometry to an externally loaded mannequin. Nevertheless, the aircraft will all the time be a aircraft, so Sick use the shorter a-plane syntax for that one.

We have an object to hit however nothing to hit it with. Now add a field for the paddle. As an alternative of utilizing a controller to swing the paddle, we’ll begin with the best attainable interplay: put the field within the digicam. Then you may swing it by simply turning your head (or dragging the scene digicam w/ the mouse on desktop). A bit of awkward however it works effectively sufficient for now.

Also observe that I positioned the paddle field at z -3. If Id left it on the default place it might appear to vanish, however its truly nonetheless tright here. The paddle is simply too near the digicam for us to see. If I look down at my toes I can see it although. Each time you’re working with VR and your object doesnt present up, first test if its behind you or too near the camera.

<a-entity position=”0 0 -3″ id=”weapon”> <a-box color=’blue’ width=’0.25′ height=’0.5′ depth=’3′></a-box></a-entity>

Great. Now the entire components of our scene are right here. In the occasion you adopted alongside you must have a scene in your desktop that appears like this.

Basic Geometry

If you play with this demo youll see that you may transfer your head and the paddle strikes with it, however making an attempt to hit the ball wont do something. Thats as a outcome of we solely have geometry. The pc is conscious of how our objects look however nothing about how they behave. For that we want physics.


Physics engines could be sophisticated, however happily Don McCurdy has created A-Frame bindings for the wonderful Cannon.js open supply physics framework. We simply need to incorporate his aframe-extras library to begin out enjoying with physics.

Add this to the head of the html page:

<!– physics and different extras –><script src=”//”></script>

Now we will activate physics by including physics=”debug:true; to the a-scene.

Of course merely turning on the physics engine wont do something. We nonetheless have to inform it which objects within the scene needs to be affected by gravity and different forces. We do that with dynamic and static our bodies. A dynamic physique is an object with full physics. It would possibly probably transmit drive and be affected by different forces, together with gravity. A static physique can transmit drive when one thing hits it, however is in any other case unaffected by forces. Typically you’ll use a static physique for one thing that doesnt transfer, like the bottom or a wall, and a dynamic physique for issues which do transfer across the scene, corresponding to our ball.

Lets make the bottom static and the ball dynamic by including dynamic-body and static-body to their components:

<a-entity id=’ball’ position=”0 1 -4″ material=”color:green;” geometry=”primitive:sphere; radius: 0.5;” dynamic-body></a-entity> <a-plane color=’red’ static-body rotation=”-90 0 0″ width=”100″ height=”100″></a-plane>

Great. Now while you reload the web page the ball will fall to the bottom. You could additionally see grid strains or dots on the ball or aircraft. These are bits of debugging info from the physics engine to allow us to see the sides of our objects from a physics perspective. It’s attainable to have the physics engine use a measurement or form for our objects thats different than the actual drawn geometry. I do know this sounds unusual, however its truly fairly helpful, as we’ll see later.

Now we have to make the paddle in a place to hit the ball. For the rationale that paddle strikes, you would possibly assume we ought to always use a dynamic-body, however actually we wish our code (and the camera) to regulate the place of the paddle, not the physics engine. We simply need the paddle to be there for exerting forces on the ball, not the opposite means round, so we’ll use a static-body.

<a-entity digicam look-controls position=”0 1.5 0″> <a-cursor></a-cursor> <a-entity position=”0 0 -3″ id=’weapon’> <a-box color=’blue’ width=’0.25′ height=’0.5′ depth=’3′ static-body></a-box> </a-entity></a-entity>

Now we will transfer the digicam to swing the paddle and hit the ball. In the occasion you hit it laborious then it would fly off to the facet as an alternative of rolling, precisely what we want!

You would possibly ask why not simply activate physics for every part. Two causes: First, physics requires CPU time. If extra objects have related physics, the extra CPU assets they’ll consume.

Second cause: For quite a bit of objects within the scene, we dont truly need physics turned on. If I’ve a tree in my scene, I dont need it to fall down simply because its a millimeter above the bottom. I dont need the moon to have the flexibility to fall from the sky simply because its above the bottom. Solely activate physics for issues that basically need it in your application.


Moving the ball by hitting it’s enjoyable, however for an actual recreation we have to observe when the paddle hits the ball to extend the gamers rating. We additionally have to reset the ball again to the center for one more shot. We use collisions to do that. The physics engine emits a collide occasion every time an object hits one other object. By listening to this occasion we will discover out when one thing has been hit, what it’s, and we will manipulate it.

First, lets make some utility features for accessing DOM components. Ive put these on the prime of the web page so they are going to be obtainable to code everywhere.

<script> $ = (sel) => document.querySelector(sel) $$ = (sel) => document.querySelectorAll(sel) on = (elem, kind, hand) => elem.addEventListener(type,hand)</script>

Lets discuss in regards to the features we want. First, we wish to reset the ball after the participant has hit it or if theyve missed and a certain variety of seconds have passed by. Resetting means transferring the ball again to the middle, setting the forces again to zero, and initializing a timeout. Lets create the resetBall perform to do this:

let hit = falselet resetId = 0const resetBall = () => { clearTimeout(resetId) $(“#ball”).body.position.set(0, 0.6,-4) $(“#ball”).body.velocity.set(0, 5,0) $(“#ball”).body.angularVelocity.set(0, 0,0) hit = false resetId = setTimeout(resetBall,6000)}

In the above code Im utilizing the $ perform with a selector to search out the ball element within the web page. The physics engine provides a physique property to the element containing the entire physics attributes. We will reset the place, velocity, and angularVelocity from right here. The code above additionally units a timeout to name resetBall once more after six seconds, if nothing else happens.

There are two issues to notice right here. First, Im setting body.position slightly than the common place element that each one A-Frame entities have. Thats as a outcome of the physics engine is answerable for this object, so we have to inform the physics engine in regards to the adjustments, not A-Frame.

The second factor to notethe velocity is simply not reset to zero. As an alternative its set to the vector 0,5,0. This implies zero velocity within the x and z instructions, however 5 within the y route. This provides the ball an preliminary vertical velocity, taking pictures it up. In fact gravity will begin to have an effect on it as quickly because the ball jumps, so the speed will rapidly decelerate. If I needed to make the sport more durable I could enhance the preliminary velocity right here, or level the vector in a random route. A lot of alternatives for improvements.

Now we have to know when the collision truly occurs so we will increment the rating and set off the reset. Effectively do that by dealing with the collide occasion on the #weapon entity.

on($(“#weapon”),’collide’,(e)=>{ const ball = $(“#ball”) if( === && !hit) { hit = true rating = rating + 1 clearTimeout(resetId) resetId = setTimeout(resetBall,2000) }}) setTimeout(resetBall,3000)

The code above checks if the collision occasion is for the ball by evaluating the physique ids. It additionally makes certain the participant didnt already hit the ball, in any other case they might hit the ball time and again earlier than we reset it. If the ball was hit, then set hit to true, clear the reset timeout, and schedule a model new one for 2 seconds within the future.

Great, now we will launch the ball time and again and preserve observe of rating. In fact a rating isnt very helpful if we cant see it. Lets add a textual content element inside of the digicam, so it’s all the time seen. That is known as a Heads Up Display or HUD.

<a-entity digicam …. <a-text id=”score” value=”Score” position=”-0.2 -0.5 -1″ color=”red” width=”5″ anchor=”left”></a-text></a-entity>

We have to replace the rating textual content each time the rating adjustments. Lets add this to the tip of the collide occasion handler.

on($(“#weapon”),’collide’,(e)=>{ const ball = $(“#ball”) if( === && !hit) {… $(“#score”).setAttribute(‘text’,’value’,’Score ‘+score) }})

Now we will see the rating on display. It ought to seem like this:

Score and Physics


We have a primary recreation working. The participant can hit the ball with a paddle and get factors. Its time to make this look higher with actual 3D fashions. We want a cool-looking imp to whack with the stick.

The last challenge resulted in tons of nice 3D scenes constructed across the theme of Low-Poly Medieval Fantasy. Many of those have already been cut up up into particular person property and tagged with medievalfantasyassets.

For this undertaking I selected to make use of this imp model for the ball and this staff mode for the paddle.

Since we’re going to be loading a lot of fashions we ought to always load them as property. Property are massive chunks of knowledge (images, sounds, models) which are preloaded and cached routinely when the sport begins. Put this on the prime of the scene and modify the src urls to level to wherever you downloaded the models.

<a-assets> <a-asset-item id=”imp” src=”models/imp/scene.gltf”></a-asset-item> <a-asset-item id=”staff” src=”models/staff/scene.gltf”></a-asset-item></a-assets>

Now we will swap the sphere with the imp and the paddle field for the employees. Replace the weapon element like this:

<a-entity position=”0 0 -3″ id=”weapon”> <a-entity gltf-model=”#staff”></a-entity></a-entity>

And the ball element like this:

<a-entity id=’ball’ position=”0 1 -4″ dynamic-body > <a-entity id=’imp-model’ gltf-model=”#imp”></a-entity></a-entity>

Imp and lacking staff

We can see the imp however the employees is lacking. What happened?

The drawback is the employees model itself. The imp model is (mostly) centered within its coordinate system, so it’s visually positioned the place we put it. Nevertheless the employees fashions middle is considerably off from the middle of its coordinate system; roughly 15 to twenty meters away. It is a widespread difficulty with fashions you discover on-line. To repair it we have to translate the fashions place to account for the offset. After enjoying round with the employees model I discovered that an offset of 2.3, -2.7, -16.3 did the trick. I additionally needed to rotate it ninety levels to make it horizontal and shift it ahead by 4 meters to make it seen. Wrap the model with a further entity to use the interpretation and rotation.

<a-entity id=weapon rotation=”-90 0 0″ position=”0 0 -4″> <a-entity position=”2.3 -2.7 -16.3″ gltf-model=”#staff” static-body></a-entity></a-entity>

Now we will see the employees, however we nonetheless have an issue. The employees is simply not a easy geometric form, its a full 3d mannequin. The physics engine cant work instantly with a full mesh. As an alternative it must know which primitive object to make use of. We could use a field like we did initially, however I selected to go along with a sphere centered on the finish of the employees. Thats the half that the participant ought to truly use to hit the imp, and by making it bigger than the staffs diameter we will make the sport simpler than it might be in actual life. We additionally need to maneuver the static-body definition to the outer entity in order that it isnt affected by the model offset.

<a-entity rotation=”-90 0 0″ position=”0 0 -4″ id=’weapon’ static-body=”shape:sphere; sphereRadius: 0.3;”> <a-entity position=”2.3 -2.7 -16.3″ gltf-model=”#staff” ></a-entity></a-entity>

Imp and Staff


We have the core recreation mechanics working accurately with the model new fashions, lets add some decorations subsequent. I grabbed extra fashions from SketchFab for a moon, a cauldron, a rock, and two different timber. Place them within the scene at different positions.

<a-assets> <a-asset-item id=”imp” src=”models/imp/scene.gltf”></a-asset-item> <a-asset-item id=”staff” src=”models/staff/scene.gltf”></a-asset-item> <a-asset-item id=”tree1″ src=”models/arbol1/scene.gltf”></a-asset-item> <a-asset-item id=”tree2″ src=”models/arbol2/scene.gltf”></a-asset-item> <a-asset-item id=”moon” src=”models/moon/scene.gltf”></a-asset-item> <a-asset-item id=”cauldron” src=”models/cauldron/scene.gltf”></a-asset-item> <a-asset-item id=”rock1″ src=”models/rock1/scene.gltf”></a-asset-item></a-assets>…<!– cauldron –><a-entity position=”1.5 0 -3.5″ gltf-model=”#cauldron”></a-entity><!– the moon –><a-entity gltf-model=”#moon”></a-entity> <!– timber –><a-entity gltf-model=”#tree2″ position=”38 8.5 -10″></a-entity><a-entity gltf-model=”#tree1″ position=”33 5.5 -10″></a-entity><a-entity gltf-model=”#tree1″ position=”33 5.5 -30″></a-entity>

Our little recreation is beginning to seem like an actual scene!

Trees and Moon

The cauldron has bubbles which appeared to animate on SketchFab however they arent animating right here. The animation is saved contained in the model however it isnt routinely performed with out a further element. Simply add animation-mixer to the entity for the cauldron.

The last recreation has rocks scattered across the discipline. Nevertheless, we actually dont wish to manually place fifty different rocks. As an alternative we will write a element to randomly place them for us.

The A-Frame docs clarify how to create a component so I wont recount all of it right here. The gist of it’s this: A element has some enter properties after which executes code when init() is named (and a couple of different functions). On this case, we wish to settle for the supply of a mannequin, some variables controlling distribute the model across the scene, after which have a perform which can create N copies of the model.

Below is the code. I do know it appears intimidating however its truly fairly easy. Effectively undergo it step by step.

<!– alternate random quantity generator –><script src=”js/random.js”></script><!– our `distribute` element –><script> AFRAME.registerComponent(‘distribute’, { schema: { src: {type:’string’}, jitter: {type:’vec3′}, centerOffset: {type:’vec3′}, radius: {type:’number’} }, init: function() { const rg = new Random(Random.engines.mt19937().seed(10)) const middle = new THREE.Vector3(,, const jx = const jy = const jz = if($( { const s = for(let i = -s; i<s; i++) { for(let j=-s; j<s; j++) { const el = document.createElement(‘a-entity’) el.setAttribute(‘gltf-model’, const offset = new THREE.Vector3(i*s + rg.real(-jx,jx), rg.real(-jy,jy), j*s – rg.real(-jz,jz)); el.setAttribute(‘position’, center.clone().add(offset)); el.setAttribute(‘rotation’,{x:0, y:rg.real(-45,45)*Math.PI/180, z:0}) const scale = rg.real(0.5,1.5) el.setAttribute(‘scale’,{x:scale,y:scale,z:scale}) $(‘a-scene’).appendChild(el) } } } } })</script>

First I import random.js. It is a random quantity generator from this random-js project by Cameron Knight. We could use the usual Math.random() perform constructed into Javascript, however I wish to be certain that the rocks are all the time positioned the identical means each time the sport is run. This different generator lets us present a seed.

In the primary line of the init() code you may see that I used the seed 10. I truly tried a quantity of different seeds till I discovered one which I favored the look of. If I did truly need each load to be totally different, say for various ranges of the sport, then I could present a unique seed for every level.

The core of the distribute element consists of the nested for loops. The code creates a grid of entities, each connected to the identical mannequin. For every copy, we’ll translate it from the pure middle level of the unique model (the modelCenter parameter) , including a random offset utilizing the jitter parameter. Jitter represents the utmost quantity the rock ought to transfer from that grid level. Utilizing 0 0 0 could be no jitter. Utilizing 0 10 0 would make the rocks go vertically wherever between -10 and 10, however not transfer in any respect within the horizontal aircraft. For this recreation I used 2 0.5 2 to maneuver them round largely horizontally however transfer up and down a tiny bit. The loop code additionally offers the rocks a random scale and rotation across the Y axis, simply to make the scene look a bit extra organic.

This is the ultimate result.

Distributed Rocks

This weblog has gotten fairly lengthy and we nonetheless havent labored on lighting, sounds, or polish. Lets proceed the sport proper now in Part 2.

I am an creator, researcher, and recovering engineer. Previously on the Swing Staff at Solar, the webOS staff at Palm, and Nokia Analysis. I unfold the phrase of excellent person experiences. I reside in sunny Eugene Oregon with my spouse and genius Lego builder child.

More articles by Josh Marinacci

Please check this great service at: or visit FREE SERVICES menu
[Total: 0    Average: 0/5]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.