Buildings Wave Animation with Three.js

Original Source:

This tutorial is going to demonstrate how to build a wave animation effect for a grid of building models using three.js and TweenMax (GSAP).

Attention: This tutorial assumes you already have a some understanding of how three.js works.
If you are not familiar, I highly recommend checking out the official documentation and examples .


Source: View
by: Baran Kahyaoglu

Core Concept

The idea is to create a grid of random buildings, that reveal based on their distance towards the camera. The motion we are trying to get is like a wave passing through, and the farthest elements will be fading out in the fog.


We also modify the scale of each building in order to create some visual randomness.

Getting started

First we have to create the markup for our demo. It’s a very simple boilerplate since all the code will be running inside a canvas element:

<html lang=”en”>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
<meta name=”target” content=”all”>
<meta http-equiv=”cleartype” content=”on”>
<meta name=”apple-mobile-web-app-capable” content=”yes”>
<meta name=”mobile-web-app-capable” content=”yes”>
<title>Buildings Wave</title>
<script src=””>></script&gt
<script src=”” &gt</script&gt
<script src=””&gt</script&gt
<script src=””&gt</script&gt

Basic CSS Styles
html, body {
margin: 0;
padding: 0;
background-color: #fff;
color: #fff;
box-sizing: border-box;
overflow: hidden;

canvas {
width: 100%;
height: 100%;

Initial setup of the 3D world

We create a function called init inside our main class. All subsequent methods will be added inside this method.

init() { = new THREE.Object3D();
this.gridSize = 40;
this.buildings = [];
this.fogConfig = {
color: ‘#fff’,
near: 1,
far: 138

Creating our 3D scene
createScene() {
this.scene = new THREE.Scene();

this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);

this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;


// this is the line that will give us the nice foggy effect on the scene
this.scene.fog = new THREE.Fog(this.fogConfig.color, this.fogConfig.near, this.fogConfig.far);


Let’s add a camera for to scene:

createCamera() {
const width = window.innerWidth;
const height = window.innerHeight; = new THREE.PerspectiveCamera(20, width / height, 1, 1000);

// set the distance our camera will have from the grid
// this will give us a nice frontal view with a little perspective, 16, 111);



Now we need to add a shape to serve as the scene’s ground

addFloor() {
const width = 200;
const height = 200;
const planeGeometry = new THREE.PlaneGeometry(width, height);

// all materials can be changed according to your taste and needs
const planeMaterial = new THREE.MeshStandardMaterial({
color: ‘#fff’,
metalness: 0,
emissive: ‘#000000’,
roughness: 0,

const plane = new THREE.Mesh(planeGeometry, planeMaterial);

planeGeometry.rotateX(- Math.PI / 2);

plane.position.y = 0;


Load 3D models

Before we can build the grid, we have to load our models.


loadModels(path, onLoadComplete) {
const loader = new THREE.OBJLoader();

loader.load(path, onLoadComplete);

onLoadModelsComplete(model) {
// our buildings.obj file contains many models
// so we have to traverse them to do some initial setup

this.models = […model.children].map((model) => {
// since we don’t control how the model was exported
// we need to scale them down because they are very big

// scale model down
const scale = .01;
model.scale.set(scale, scale, scale);

// position it under the ground
model.position.set(0, -14, 0);

// allow them to emit and receive shadow
model.receiveShadow = true;
model.castShadow = true;

return model;

// our list of models are now setup

Ambient Light
addAmbientLight() {
const ambientLight = new THREE.AmbientLight(‘#fff’);


Grid Setup

Now we are going to place those models in a grid layout.


createGrid() {
// define general bounding box of the model
const boxSize = 3;

// define the min and max values we want to scale
const max = .009;
const min = .001;

const meshParams = {
color: ‘#fff’,
metalness: .58,
emissive: ‘#000000’,
roughness: .18,

// create our material outside the loop so it performs better
const material = new THREE.MeshPhysicalMaterial(meshParams);

for (let i = 0; i < this.gridSize; i++) {
for (let j = 0; j < this.gridSize; j++) {

// for every iteration we pull out a random model from our models list and clone it
const building = this.getRandomBuiding().clone();

building.material = material;

building.scale.y = Math.random() * (max – min + .01);

building.position.x = (i * boxSize);
building.position.z = (j * boxSize);

// add each model inside a group object so we can move them easily;

// store a reference inside a list so we can reuse it later on


// center our group of models in the scene – 10, 1, -this.gridSize – 10);

Spot Light

We also add a SpotLight to the scene for a nice light effect.


addSpotLight() {
const light = { color: ‘#fff’, x: 100, y: 150, z: 100 };
const spotLight = new THREE.SpotLight(light.color, 1);

spotLight.position.set(light.x, light.y, light.z);
spotLight.castShadow = true;


Point Lights

Let’s add some point lights.


addPointLight(params) {
// sample params
// {
// color: ‘#00ff00’,
// intensity: 4,
// position: {
// x: 18,
// y: 22,
// z: -9,
// }
// };

const pointLight = new THREE.PointLight(params.color, params.intensity);

pointLight.position.set(params.position.x, params.position.y, params.position.z);


Sort Models

Before we animate the models into the scene, we want to sort them according to their z distance to the camera.

sortBuildingsByDistance() {
this.buildings.sort((a, b) => {
if (a.position.z > b.position.z) {
return 1;

if (a.position.z < b.position.z) {
return -1;

return 0;

Animate Models

This is the function where we go through our buildings list and animate them. We define the duration and the delay of the animation based on their position in the list.

showBuildings() {
this.sortBuildingsByDistance();, index) => {, .3 + (index / 350), { y: 1, ease: Power3.easeOut, delay: index / 350 });

Here is how a variation with camera rotation looks like:


Models by Backlog Studio. Check out their Instagram.

Buildings Wave Animation with Three.js was written by Ion D. Filho and published on Codrops.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

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