Image for post
Image for post

Progressive Enhancement with WebGL and React

David Lindkvist
Sep 10 · 6 min read

These days, it’s very common to mix creative technologies like WebGL with traditional HTML/CSS when building websites. In this post, I will try to outline how we at 14islands approach this trend to ensure the content is still available to as many visitors as possible. All of these techniques were used on our new company website 14islands.com.

Thoughts on accessibility

Go back just a few years and responsive web design, progressive enhancement and mobile-first were all the rage. Here’s the thing — they are still valid practices, and just because we have access to new shiny toys doesn’t mean we get a free pass to only build websites for desktop screens and the latest browser technology.

Image for post
Image for post

Assumptions made by most WebGL websites

Everybody loves a cool loading animation — Having a preloader allows the browser to download all assets before showing the website. Large images are known to cause lag as they are uploaded to the GPU for the first time. For this reason, it’s common for WebGL apps to preload and pre-render textures before showing the website.

Everybody loves to scroll — Having only one long page means the WebGL canvas can create all objects for the whole website on page load (during the preloader animation), and there are no requests to load new pages or new assets to worry about.

Everybody has a modern browser — Building for advanced browsers and adding polyfills for missing features. Some websites even lock the user out if the browser lacks support for a modern feature.

Because of these assumptions, it’s common to create and position all content using Javascript (e.g. https://github.com/react-spring/react-three-flex). Sometimes, text content is rendered only as WebGL textures, which makes the content essentially images.

In reality, it depends™️

If you’re doing a cool campaign site or interactive experience, many of these assumptions might be correct. However, for many projects, having quick access to the content, a high level of accessibility and SEO are equally important to delivering a memorable creative experience.

Not all websites can afford a long loading animation. Visitors have short attention spans, and it can impact conversion rates and sales.

A progressively enhanced experience

We usually use Gatsby.js on our projects. It’s fast, has a good ecosystem of plugins to speed up common tasks, and offers server-side rendering out of the box.

As any fan of static websites knows, server-side rendering is great for SEO and accessibility since the content can be accessed without executing a bunch of Javascript. We also want to make sure text content is available even if WebGL isn’t supported.

In fact, our javascript heavy React and WebGL website even renders without Javascript support: 😲

Image for post
Image for post

With Javascript enabled but no WebGL support, you get some fancier hover states and the ability to play our showreel in a cool dark cinema mode: 😎🍿

Image for post
Image for post

And if the browser supports WebGL, you get all the gooey blob magic: 🎩🐇

Image for post
Image for post

When feature detection falls short

Unfortunately, some browsers will gladly say that they support WebGL, but they are simply too weak to run the code with good performance.

There’s no good way to feature-detect powerful devices. Some try to measure the frame rate over time or use a combination of other features, but none of them are very robust. In our case, we simply base it on screen size and assume that a larger screen corresponds to a more powerful device (which may or may not be true).

React & Three.js

We like to use the Three.js WebGL library. The pace at which it updates and introduces breaking changes is mind-blowing. Any third-party react library that tries to provide a 1:1 mapping of Three.js objects to React components will probably not be maintained for long.

react-three-fiber takes a different approach by hooking into the React reconciler and proxies JSX tags to Three.js objects. It doesn't even care what version of Three we are using, which makes it future proof. Having said that, there are definitely pitfalls and enough performance gotchas for a future blog post. 😬

Enhancing with WebGL

We created a framework on top of react-three-fiber that enables us to add WebGL functionality to any existing React component on the website.

Image for post
Image for post

At the core we have one global shared WebGL canvas that stays in between page loads:

export const wrapRootElement = ({ element }) => (
<>
{element}
<GlobalCanvas/>
</>
)

Each UI component can opt-in to use this global canvas using a custom useCanvas() hook:

const Image = () => {
const ref = useRef()

// add webgl component to global canvas while mounted
useCanvas(<WebGlImage image={ref} />)
return <img ref={ref} src={...} />
}

When the <Image> component mounts, it tells the global canvas to render <WebGlImage>. It will also automatically remove the WebGL mesh and clean up resources when it unmounts.

This keeps our code modular and hides the details of the WebGL implementation from regular UI components.

The <WebGlImage> is responsible for hiding the normal image element and draw a WebGL mesh in its place.

Syncing DOM content with WebGL canvas 🔮

A big challenge is to sync the fixed canvas elements with the scrollable HTML content. The technique we use largely follows an approach that tracks “proxy” elements in the normal page flow and updates the WebGL scene positions to match them.

Since Javascript is inevitably slower than native scrolling, this can make the WebGL scene feel laggy. The most common way to fix this is to have some easing on the WebGL position update.

Here’s an example of a WebGL image with easing next to some normal content. Notice how the image all of a sudden feels more smooth than the DOM content because of the easing:

Image for post
Image for post

To fix this, most websites also introduce a virtual scroll on the HTML content with a matching easing function. This makes the whole page move with the same feeling, and while it’s for sure less performant than a native scroll, it still perceived as being smoother when all items move at the same speed:

Image for post
Image for post

A virtual scroll means that we move all HTML content with Javascript, and it’s a known source of many accessibility issues. For some projects, it’s worth the trade-off, while other projects might require a higher level of accessibility.

There are many libraries for virtual/smooth scrolling available. We suggest you pick one which at least retains the browser’s native scrollbar.

Closing thoughts 💡

To be honest, if load time and maximum device support is your highest priority, you shouldn’t use WebGL at all. But for some websites (like ours), there is a good middle ground.

By adding WebGL to existing HTML elements on the page, we can split up the work better. Some developers can focus on building a solid responsive website layout, and the WebGL can be added on top in a second iteration. Of course, everything needs to be thoroughly tested together, as there are many performance pitfalls when using this technique.

There are downsides to this approach. Loading large images might cause scroll jank, and it becomes harder to achieve smooth page transitions when everything isn’t preloaded. Accessibility also takes a hit due to having virtual scrolling of the DOM.

Let us know if you have ideas on how to improve this even further, we’d love to hear more how others approach this!

14islands

Creative design and development studio.

David Lindkvist

Written by

Co-founder and Creative Developer @14islands

14islands

14islands

Creative design and development studio. We create digital experiences with cutting edge technologies.

David Lindkvist

Written by

Co-founder and Creative Developer @14islands

14islands

14islands

Creative design and development studio. We create digital experiences with cutting edge technologies.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store