Our website launch: New colors, new tech, new challenges

A couple of weeks ago, we launched our new brand image along with our fresh new site which shows the work we have done with some of our clients. It was also an opportunity to update the tech stack that we were using for our landing pages.

Our website launch: New colors, new tech, new challenges

This article originally appeared in the Wolox Medium Blog.

A couple of weeks ago, we launched our new brand image along with our fresh new site which shows the work we have done with some of our clients. It was also an opportunity to update the tech stack that we were using for our landing pages.

There are three essential parts of our new site:

  • VueJS is our main tech to create components. It ensures scalability, maintainability, and great performance.
  • ThreeJS powers the graphics of our Home view.
  • With Lottie, we created both beautiful and meaningful animations throughout our site.

Let me go over our reasons behind this powerful combination.

The progressiveness of VueJS

As I said, we wanted to start fresh with our site so we assessed that jQuery was not quite meeting our expectations in terms of maintainability, scalability, and developer experience in general. For those reasons, we thought of using a framework that we were familiar with, so we narrowed the options to React and Vue.

However, we felt like we didn’t really need a full SPA since the content and what was rendered was basically static. We needed a better fit for the challenge. For that reason, we decided to go with a Multi-Page App instead, working with Vue as a perfect replacement for jQuery.

What is a Multi-Page App? Well, a Single Page App has only one HTML file with all the applications in it and JavaScript does all the magic with the in-memory router (like vue-router, react-router, etc.). A Multi-Page has an HTML per route, so we will have a new Vue instance on each page we create. One of its advantages is great “first load” times, reaching a First Contentful Paint on about 1500ms — 2000ms and an overall code bundle (without assets) under 300kb per page.

You may think: “Yeah pretty good FCP times but you can do it with jQuery too!” Sure, we can but there are far better advantages in code quality, maintainability, and scalability than plain Vanilla JS. And we can talk about the ongoing changes in Vue that make it a far better option as a modern framework.

Vue has been a blast. Its progressiveness helped us a lot to have new choices for our projects and create amazing solutions.

The challenge of ThreeJS

As you may have noticed, there are a lot of flying dots on our home screen which are powered by ThreeJS. If you don’t know what that is, let me help you: it’s a powerful JS library that lets us render high-performance graphics using OpenGL which is written in GSGL and will run on the GPU. We have never done something like that before so we needed to understand how ThreeJS works and, trust me, that’s no simple task.

ThreeJS principally uses geometry and material that combined will form a mesh. For the giant dot ball we called the “supernova” we started with two shaders created by the community: a vertex shader — that calculates the position of every element — and a fragment shader — the dots that you see. Then we configured the rotation speed, the dots’ size, morph, and dnoise attribute, and we were done. Not that much work here but it was great as an introduction to ThreeJS.

However, we had to create the particles that were coming out of the supernova from scratch. How did we accomplish that? As I said, ThreeJS elements use geometry and material, so we coded them:

export function createPointMaterial(size, opacity = 1) {
  const matCanvas = document.createElement('canvas')
  matCanvas.width = size
  matCanvas.height = size
  const matContext = matCanvas.getContext('2d')
  const texture = new CanvasTexture(matCanvas)
  const center = size / 2
  matContext.beginPath()
  matContext.arc(center, center, size / 2, 0, 2 * Math.PI, false)
  matContext.closePath()
  matContext.fillStyle = `rgba(255, 255, 255, ${opacity})`
  matContext.fill()
  texture.needsUpdate = true
  return texture
}

The material for our particles

const particlesGeometry = new THREE.BufferGeometry()
const particlesVertices = []
const particlesRandoms = []

const flyingParticles = 150
let isPlaying = false
let animFrame = null

const offscreenLimit = 100
const particleAcceleration = 0.002
const minParticlePosition = -100
const maxParticlePosition = 100

for (let i = 0; i < flyingParticles; i++) {
  particlesVertices.push(0, 0, getRandomFloat(-20, -10))

  particlesRandoms.push(
    getRandomFloat(minParticlePosition, maxParticlePosition),
    getRandomFloat(minParticlePosition, maxParticlePosition),
    getRandomFloat(minParticlePosition, maxParticlePosition)
  )
}

particlesGeometry.addAttribute('position', new THREE.Float32BufferAttribute(particlesVertices, 3))

The geometry of the particles

As you may have noticed, the particles are created with a canvas element for the material and then customized to meet the needed color, size, and shape. The geometry is basically a bunch of vectors that will mark our particles’ position and then we can move them by changing these vectors’ X and Y values.

Now, the problem is the performance: how can we make thousands of particles render and move without having a performance bottleneck? Well, there are two approaches to solve this issue. First, we have the particles all over the view, so the solution was to set a buffer size ridiculously big so it can handle the number of particles for as long as the user stays on the site. The particles were created dynamically via an EcmaScript Generator so the buffer won’t always have its memory in use, just allocated.

function* generateNewParticles() {
  let randoms = [],
    particles = []
  while (true) {
    randoms = [random(), random(), random()]
    particles = [0, 0, getRandomFloat(-500, -200)]
    yield { randoms, particles }
  }
}

const particlesGenerator = generateNewParticles()

function renderFlyingParticles() {
  const positionArray = particlesGeometry.attributes.position.array
  moveParticles()
  const { randoms, particles } = particlesGenerator.next().value
  positionArray.set(particles, flyingParticles + 1)
  particlesRandoms.push(...randoms)
  flyingParticles++
  particlesGeometry.attributes.position.needsUpdate = true
  requestAnimationFrame(renderFlyingParticles)
  renderer.render(scene, camera)
}

Example of a generator that creates particles

This first implementation had a problem on low-end PCs that couldn’t load the full buffer size and render the particles. As we iterated the designs, the particle count and position changed and now they are just on the “hero” section. The buffer is much smaller so the particles are easier to control and we can reuse them, so when they reach the “end of the screen”, they will be re-centered.

Smooth-as-butter animations with Lottie

For our workflow animations, we used Lottie, an Airbnb library that helps our team to create and ship animations at 60fps and under 200kb. Of course, this is a huge benefit when the time is crucial or the animation is really complicated to code.

Our workflow animation made with Lottie

All in all, our new site has a great design and we put a lot of effort into creating something new that we haven’t done before. The colors may be new, but our commitment to deliver rock-solid digital products with top-notch technology is still as strong as ever. We really hope you pay us a visit at wolox.co.