Introducing <hic-pageflip>: A Pageflip/Flipbook Custom Element powered by HTML-in-Canvas

Recording of the demo.

~

Way back in 2005, I used a Flash PageFlip component to display a book on a website. Ever since, recreating a modern version has been on my backlog to investigate. With the arrival of the experimental HTML-in-Canvas API, we can finally bring it back — this time displaying real HTML.

~

⚠️👨‍🔬 Experimental Feature!

The technology used to power this <hic-pageflip> component is still experimental and is undergoing significant changes. To see the component in action, you need Chrome with chrome://flags#canvas-draw-element enabled.

~

PageFlip?

PageFlip is a classic UI pattern that simulates a hyper-realistic, interactive book or magazine: users can click, drag, and peel page corners to flip through content with dynamic lighting, curls, and shadows. It was popularized in 2005–2006 by Iparigrafika who shipped a Flash Component.

Screenshot of the (now defunct) PageFlip website (archived)

While recreating this effect on the web has technically been possible for years using 2D Canvas or WebGL shaders, it always came with massive accessibility and usability drawbacks. Because the pages were trapped inside a canvas pixel grid or rendered as static images, you lost selectable text, clickable links, screen reader support, and browser features like find-in-page.

Using HTML to render pages has also been possible by leveraging CSS transforms (see this example). However, that effect was always somewhat “fake”: it’s a pure 2D clip effect with a shadow laid on top, instead of true 3D folding and curling as described in Chris Luke’s “The anatomy of a page curl”.


The 2D fold effect
The 3D fold effect
Comparison of a 2D vs 3D fold effect. Look closely at the shadow and the curvature of the page, and how it affects the text. Use the slider to reveal either the 2D or 3D effect screenshot.

~

HTML-in-Canvas was made for this

Up until now, you always had to compromise: either get authentic 3D page curls with Canvas or WebGL at the cost of throwing away accessibility, or stick to real HTML with CSS transforms and settle for a flat 2D clipping illusion.

The new (and still experimental) HTML-in-Canvas API changes the game. By letting you draw live DOM elements directly into canvas contexts and WebGL textures, you no longer have to choose. You get true 3D conical and cylindrical page curls, while the pages remain fully interactive, accessible DOM nodes with selectable text and clickable links.

Armed with Google Antigravity and the math from Chris Luke’s article, I set out to build it.

~

Keeping Things Easy: <hic-pageflip>

In under two hours, I had a working pageflip prototype powered by HTML-in-Canvas up and running. I then continued the work to further refine the interactions and visuals, and eventually had Antigravity refactor the whole thing into an easy-to-use web component: <hic-pageflip>

You can see it in action on the demo website which is also embedded here:

The source code is available on GitHub and you can install the package from NPM.

~

Installation

You can install the package from npm:

npm install hic-pageflip

Import the package in your application to have it register the custom elements:

import 'hic-pageflip';

You can also load it directly from a CDN and use it in your HTML, without npm at all. For example, using jsDelivr:

<script type="module" src="https://cdn.jsdelivr.net/npm/hic-pageflip"></script>

Once installed and imported, you can start using it.

~

Minimal Example

With the component, creating a pageflip interface powered by HTML-in-Canvas becomes as easy as this:

<hic-pageflip engine="3d" page-width="300" page-height="450" page-background="#c1c8c7">
	<!-- Slide 1: Cover -->
	<hic-pageflip-page>
		<div class="content">
			<h1>Cover Page</h1>
			<p>This is HTML inside a 3D WebGL pageflip!</p>
		</div>
	</hic-pageflip-page>

	<!-- Slide 2 -->
	<hic-pageflip-page>
		<div class="content">
			<h2>Inside Left Page</h2>
			<p>Selectable text and clickable <a href="https://github.com/bramus/hic-pageflip" target="_top">links</a> work natively.</p>
		</div>
	</hic-pageflip-page>

	<!-- Slide 3 -->
	<hic-pageflip-page>
		<div class="content">
			<h2>Inside Right Page</h2>
			<p>And images (with the right CORS headers)!</p>
			<p><img src="https://gravatar.com/avatar/c47bf5e271115acb3820392b0a5c9574" alt="Bramus" height="50" width="50" crossorigin="anonymous"></p>
		</div>
	</hic-pageflip-page>

	<!-- Slide 4: Backcover -->
	<hic-pageflip-page>
		<div class="content">
			<h2>Backcover</h2>
			<p>And CSS as well …</p>
			<p class="rainbow">Rainbow text, Yay!</p>
		</div>
	</hic-pageflip-page>
</hic-pageflip>

The enclosing <hic-pageflip> component creates the pageflip interface, and the <hic-pageflip-page> components are the pages contained within.

With some mininal styling applied, the code above looks like this:

~

Customization

Use the attributes on the <hic-pageflip> component to control a bunch of things, including which engine to use:

  1. 2D Engine (engine="2d"): Uses 2D canvas affine matrix reflections, half-plane clipping, and dynamic drop shadows.
  2. 3D Engine (engine="3d"): Uses WebGL vertex shaders for true cylindrical and conical page curls based on Chris Luke’s page curl algorithm.

The default is 3d, the one with the true page curl effect.

The <hic-pageflip> component itself only offers the pageflip interface, without any controls. However, you can control the pageflip programmatically by calling methods directly on the component:

const pageflip = document.querySelector('hic-pageflip');
// Go to next page
pageflip.next();
// Go to previous page
pageflip.previous();
// Go to page 4
pageflip.goToPage(4);
// Flip to a page
pageflip.flipTo(4);

Hook these up to your own buttons and you’re good to go 🙂

~

Links and resources

To recap, here are all the links you need:

~

Spread the word

Feel free to reshare one of the following posts on social media to help spread the word:

~

🔥 Like what you see? Want to stay in the loop? Here's how:

I can also be found on 𝕏 Twitter and 🐘 Mastodon but only post there sporadically.

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Leave a comment

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.