How to build a 3D tunnel in HTML

Use React, Tailwind, and CSS to make a zoomable tunnel effect

Ted Spare's avatar
Ted Spare

July 19, 2022

4 mins

This effect was achieved in just 66 lines of code. Scroll to the bottom to see the code.

This effect was achieved in just 66 lines of code. Scroll to the bottom to see the code.

Backstory

With our launch of a Linear integration, we wanted to share a memorable landing page.

We also wanted to give you a sense of deep work. The rabbit hole. Tunnel vision.

Add some inspiration from Star Wars and the GT Planar font, and an idea was born.

The targeting computer from A New Hope served as inspiration for our design.

The targeting computer from A New Hope served as inspiration for our design.

What better way than to put elements in a 3D tunnel with nothing but React, Tailwind, and CSS?

Now, surely we could reverse-engineer the example above.

How did they do it? Here's a hint:

A matrix tunnel from the GT Planar font page.

A matrix tunnel from the GT Planar font page.

A lot of computed CSS. Surely we could do this in React and Tailwind?

Getting started

Let's get into it, starting with the grid:

Each of 100 elements in an array is mapped to a grid square.

Each of 100 elements in an array is mapped to a grid square.

Here's how it looks so far:

Each square in the grid is simply a styled div.

Each square in the grid is simply a styled div.

Great. What about the 3D effect?

Adding perspective

After some trial and error, Tailwind didn't cut it. We opted to use some raw CSS:

We apply 3D perspective to the grid with CSS rotation and transform origin.

We apply 3D perspective to the grid with CSS rotation and transform origin.

That gets us one wall in 3D, as expected:

The first wall appears in 3D.

The first wall appears in 3D.

How about four walls, each with different angles, positions, and origins?

Breaking Adding the fourth wall

This part could be simplified. For now, we hard-code these values:

Here’s the almost-full code:

We stylize four grids as walls, each with its own position and angle.

We stylize four grids as walls, each with its own position and angle.

This gives us all four walls, arranged as expected.

The four walls now give the effect of a (static) tunnel.

The four walls now give the effect of a (static) tunnel.

Great. Now what about the scroll effect? We would like to go into the tunnel (via zoom) slowly as we scroll. Here’s our approach:

  • Listen to the window's scroll event
  • Calculate a zoom value from the scroll amount
  • Apply the zoom with translateZ
  • Make it feel natural with preserve-3d and manual parameter tuning
  • Now it's coming together:

    By tying the z-axis translation of the parent div to the scroll height, the tunnel seems to zoom as we scroll.

    By tying the z-axis translation of the parent div to the scroll height, the tunnel seems to zoom as we scroll.

    Zooming out

    That's it! A 3D tunnel effect that zooms in as we scroll. This can be extended with colours, shapes, or even panes of text like this.

    Here's the original example and a CodeSandbox with all of the code.

    What did you learn? What could be improved? Let us know on Twitter!

    Visit on desktop to download
    👋