While The Straits Times Digital Graphics Team has experimented with several 3D projects before, many of our past works featured scrollytelling scenes rendered as videos, which we then loaded on scroll.

However, for this project where we explained tips on how to maximise small spaces, we turned to WebGL (Web Graphics Library) – a technology that allows web browsers to render interactive 2D and 3D graphics directly on a webpage. This approach enables smoother interaction and dynamic rendering without the need to preload large video files, making the experience more lightweight and responsive across different screen sizes.

For inspiration, we looked to satirical home design content creators on TikTok such as @homedesign369 and @visionofluxury. We loved the playfulness of their videos and wanted to incorporate some of that into our story.

The 3D scene was built with assets modelled in-house using Blender, a free 3D program, supplemented by free stock furniture models that we customised to suit our needs. The animated 3D scenes were then exported in GLB format and embedded into the webpage.

Screenshot of our working 3D file in Blender.

While the sequences have different requirements, they all included one key step: ‘baking’ image textures.

In 3D graphics, the most common method to create a sense of materiality is by wrapping an image over the object.

A 3D model is unwrapped onto a UV map: a flat representation of the object. This allows the program to know where to distribute the pixel data of an image; think of it as printing on a piece of cardboard, and then folding it into a box.

An image texture could just be the base colour of an object. For example: adding stripes to a zebra. However, we can also pre-render additional details onto the same image, usually to overcome performance or aesthetic limitations.

This technique is known as “texture baking”. For this project, we “baked” lighting information to the 3D objects, so that we don’t have to rely on web-based, real-time lighting solutions, which could not accurately reflect real-life lighting.

Real-time lighting

Baked lighting

Comparison between real-time and baked lighting in a 3D scene. Real-time lighting appears flatter and less visually appealing, while baked-lighting achieves more realistic results.

Bringing 3D Scenes to the web

Rendering 3D content in a browser is made straightforward with Three.js, a JavaScript library for displaying 3D graphics. Once we finalise the 3D assets, they are exported as GLB files. Then, we load them into the browser for further control and interactivity through Three.js.

Scrollytelling with 3D Scene

For scrollytelling, the GLB file must include camera animation data, with the camera configured to move and focus on different views within a defined duration. For objects that require animation (for example shelves and bed), the duration of the animation must be the same as the camera animation data to ensure synchronisation.

Camera animation set-up in Blender showing keyframes that define the camera’s movement and focus within the 3D scene.

Without further manipulation, playing the camera animation on the web can feel much like watching a video play forward. To complete the scrollytelling experience, we convert the user’s scroll position into a value range between 0 and 100 per cent, this value is then mapped to the camera animation’s timeline, allowing the user to move through the animation in real time as they scroll.

Next, we break down how each sequence was built, showing how choices in lighting, texture baking and object animation came together to shape the look and feel of the final 3D scenes.

Sequence 1:

Introduction

The first sequence was the most straightforward. We set up the scene with sunlight coming in from the windows and some indoor lights, baked the lighting influence into the base colour in Cycles, a built-in rendering engine in Blender, and then cleaned up the textures in Photoshop.

Lighting set-up and Cycles bake settings in Blender.
The door here was contacting the floor, which meant that part of the floor would have a black shadow after baking the lighting influence since no light could realistically reach the area. As this door will be animated to open, the shadow was touched up in Photoshop.

Sequence 2:

Maximising storage

Using baked lighting works well when objects are static. However, for the second sequence, we had a lot of moving animation. In this case, picking a lighting scenario with a high level of directionality as we did with the previous sequence will not work as well.

Therefore, for this scene, we baked the lighting for each furniture separately while simulating a global illumination set-up (where lights are coming from all directions), making the lack of change in lighting when things are moving less obvious.

The method we used involved baking an ambient occlusion map, which we then combined with the base colour in Photoshop using the “multiply” blend mode.

Sequence 3:

Lighting

For the third sequence, we wanted to show the effects of some common home lighting set-ups.

While transitioning from one diffuse map (base colour image) with baked lighting influence to another could work in a purely linear sequence (lighting set-up A to B to C), we wanted to include an interactive function to allow readers to turn on and off light fixtures in different combinations. This presented some problems with the method as the different lights could not interact with one another in this case.

To solve this problem, we used a base colour map without baked lighting influence, a combination of what’s called a “light map” for the primary light sources, as well as some real-time lights added in code for the smaller accent lights.

Light map bake settings in Blender

Lighting Demo

In real-time lighting, the lighting is computed on the fly, shadows and shading are simplified, it is a trade-off between responsiveness and accurate simulation of how light interacts with surfaces.

Baked lighting, on the other hand, takes time to simulate how light bounces and diffuses across surfaces, resulting in more realistic shading.

Lighting details are pre-computed, producing softer shadows and richer shading for a more natural look.
A baked light example showcasing a cove ceiling set-up.

See how different lighting set-ups can create noticeably different results.

Colour Texture
Baked with:

Optimisation

During testing, we noticed a slow initial loading of the files, especially on iOS, which was unsurprising given the amount of 3D content featured on the page. To improve performance, we focused on two areas: optimising how assets are prepared and loading the page progressively as users scroll.

Optimising 3D assets

The preparation of 3D assets generally follows two principles: using texture variants optimised for different platforms and reducing 3D model complexity.

Multiple GLB versions are prepared with different texture resolutions, and only one is loaded at runtime based on the reader’s screen size and platform. Smaller screens and mobile devices are assigned lower-resolution versions.

As for the 3D model’s complexity, we were able to reduce it with the help of Blender’s Decimate modifier, which simplifies meshes while maintaining acceptable visual quality through side-by-side comparison.

A bean bag model with around 2,000 vertices, keeping the shape and details.
Wireframe view of the bean bag with 2,000 vertices.
Simplified bean bag model reduced to 100+ vertices, but still visually detailed.
Wireframe view of the simplified bean bag.

Optimise lightmap HDR files

Among all 3D-related assets, lightmap HDR files have the largest file sizes and high resolutions. The high resolution is necessary to preserve lighting details across the 3D interior. A single 4K HDR file can be about 25MB, which slows loading and consumes enough memory to cause crashes on some iOS devices.

These issues are addressed with two approaches. Loading time is reduced by compressing HDR files using gainmap-js, which compresses the file size into 10 per cent of the original. Memory related crashes are mitigated by preparing smaller resolution lightmaps at 3K for desktop, 2K for tablet, and 1K for mobile devices.

Progressive loading of page sections

Instead of loading all sections upfront, the page progressively loads content as the user advances. This approach ensures content becomes available to viewers as quickly as possible. Each of the two types of 3D sections, scrollytelling and interactive, has its own trigger to load the next chunk of content.

Scrollytelling: Next content is loaded when the user scrolls until the end of the scrollytelling section.
Interactive: Next content is loaded when the user clicks a button that marks the completion of the interactive section.

As we explore new ways of telling stories, finding the balance between creative expression and technical feasibility remains an ongoing challenge to the team. Each project brings us a step closer to refining that balance, with the shared goal of telling stories that not only inform but also immerse readers in the worlds we build.

Read the interactive here.