npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hic-pageflip

v1.0.3

Published

A Pageflip/Flipbook Custom Element powered by HTML-in-Canvas

Readme

<hic-pageflip>

A Pageflip/Flipbook Custom Element powered by HTML-in-Canvas.

A slide in the book with HIC in the top right

[!WARNING] HTML-in-Canvas is experimental technology. This project relies on experimental browser APIs (<canvas layoutsubtree>, ctx.drawElementImage, gl.texElementSubImage2D / gl.texElementImage2D, and canvas.updateElementGeometry) currently being developed in Chromium. Both Chrome 155+ and legacy Chrome < 155 APIs are supported. Features and API surfaces are subject to change.


Overview

<hic-pageflip> allows you to render interactive, flip-book style page presentations where every page is composed of real, accessible DOM elements (with selectable text, live links, CSS animations, and rich markup), while being deformed and rendered through high-performance 2D Canvas or 3D WebGL deformation pipelines.

Try the live demo | Read the introductory blogpost


Prerequisites

To view and interact with <hic-pageflip>, you need a browser that supports HTML-in-Canvas (e.g. Chrome):

  1. Browser: Google Chrome (v155+ recommended; earlier versions (149–154) supported via legacy code path).
  2. Flag: Enable chrome://flags/#canvas-draw-element

Installation & Usage

Installation

npm install hic-pageflip

Usage

After installation, import the main entry point once in your application.

import 'hic-pageflip';

The package will auto-register the <hic-pageflip> and <hic-pageflip-page> web components for you. No need to do anything else!


Example

<script type="module" src="./js/hic-pageflip/index.js"></script>

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

  <!-- Slide 2 -->
  <hic-pageflip-page drawable>
    <div class="content">
      <h2>Inside Left Page</h2>
      <p>Selectable text and clickable <a href="#test">links</a> work natively.</p>
    </div>
  </hic-pageflip-page>

  <!-- Slide 3 -->
  <hic-pageflip-page drawable>
    <div class="content">
      <h2>Inside Right Page</h2>
    </div>
  </hic-pageflip-page>

  <!-- Slide 4: Backcover -->
  <hic-pageflip-page drawable>
    <div class="content">
      <h2>Backcover</h2>
      <p>This is the backcover page.</p>
    </div>
  </hic-pageflip-page>
</hic-pageflip>

[!NOTE] In Chrome 155+, all descendant elements rendered inside canvas must have the drawable attribute. <hic-pageflip-page> sets this automatically upon connection, and <hic-pageflip> ensures it on slotted slides.

To prevent a FOUC while the custom elements are not defined, add the following CSS:

/* Prevent Flash of Undefined Custom Elements (FOUC) */
hic-pageflip:not(:defined),
hic-pageflip-page:not(:defined) {
  display: none !important;
}

/* Fade-in hic-pageflip once defined */
hic-pageflip:defined {
  transition: opacity 0.35s ease-out;
  @starting-style {
    opacity: 0;
  }
}

Also, don’t forget to size your <hic-pageflip> element, otherwise it will collapse to 0x0.

hic-pageflip:defined {
  display: block;
  width: 100%;
  max-width: 1024px;
  aspect-ratio: 1024 / 768;
}

Custom Element Reference

<hic-pageflip> Attributes & Properties

| Attribute | Property | Type | Default | Description | | :--- | :--- | :--- | :--- | :--- | | engine | engine / engineMode | string | '2d' | Rendering engine mode: '2d' or '3d'. | | page-width | pageWidth | number | 1024 | Width of a single page in CSS pixels. | | page-height | pageHeight | number | 768 | Height of a single page in CSS pixels. | | page-background | pageBackground | string | 'white' | Background color/fill applied to each page sheet. | | page | page / currentPage | number | 0 | Current page index (0 = Cover / Spread [0, 1]). |

Methods

const pageflip = document.querySelector('hic-pageflip');

// Navigate forwards by one spread
pageflip.flipForward();

// Navigate backwards by one spread
pageflip.flipBackward();

// Jump to a specific page number
pageflip.gotoPage(4);

// Switch rendering engine
pageflip.engine = '3d'; // or '2d'

// Reload textures from DOM
pageflip.reloadTextures();

Events

  • pagechange: Fired when the active spread changes upon completing a flip.
    pageflip.addEventListener('pagechange', (e) => {
      console.log('Current page:', e.detail.currentPage);
      console.log('Current spread:', e.detail.currentSpread); // e.g. [2, 3]
    });
  • flipprogress: Fired continuously during drag or transition animation ticks with progress data.

Rendering Engines

<hic-pageflip> comes equipped with two distinct rendering engines that can be switched dynamically at runtime via the engine attribute or property:

1. 2D Engine (engine="2d")

  • Class: HICPageflipEngine2D
  • Technology: 2D Canvas context with direct DOM element drawing via ctx.drawElementImage().
  • Techniques:
    • Geometric Fold Clipping: Uses half-plane clipping (clipHalfPlane) to separate the stationary spread, underneath revealed pages, and turning flap.
    • Affine Reflection: Performs 2D affine matrix reflection across the dynamic fold crease line.
    • Dynamic Lighting: Renders drop shadows cast under the fold crease and spine gutter shadows.
    • Pre-warmed Paint Records: Pre-warms slide paint records to eliminate unstyled content flashing during initial corner peeks.

2. 3D Engine (engine="3d")

  • Class: HICPageflipEngine3D
  • Technology: WebGL / WebGL2 context capturing live DOM nodes to GPU textures via gl.texElementSubImage2D() (Chrome 155+) or gl.texElementImage2D() (Chrome < 155 legacy).
  • Techniques:
    • Chris Luke's Page Curl Algorithm: Complete GLSL vertex shader implementation based on The Anatomy of a Page Curl.
    • Cylindrical & Conical Deformation: Dynamically transitions between a uniform cylinder (for horizontal flips) and a tapered cone (for diagonal corner pulls), keeping the fold apex anchored to the page edge without bulging.
    • Hardware Dual-Sided Texturing: Shaders sample front (uSamplerFront) and back (uSamplerBack) textures in a single draw pass using gl_FrontFacing.
    • Z-Fighting Prevention: Employs sub-pixel depth offsetting (uDepthOffset) to eliminate z-fighting between overlapping sheets.

Package Folder Structure

hic-pageflip
├── package.json
├── README.md
├── index.js                  # Main entry point exporting Custom Elements & version utilities
├── components/
│   ├── hic-pageflip.js       # <hic-pageflip> Web Component
│   └── hic-pageflip-page.js  # <hic-pageflip-page> Web Component
└── core/
    ├── pageflip.js           # Core Pageflip state machine & gestures
    ├── engines/
    │   ├── engine-base.js    # BaseEngine abstract base class
    │   ├── engine-2d.js      # HICPageflipEngine2D (2D Canvas)
    │   └── engine-3d.js      # HICPageflipEngine3D (3D WebGL)
    └── utils/
        ├── math.js           # Fold math, easing curves, constraints
        └── version.js        # Chromium version sniffing & Chrome 155+ detection

Class Inheritance & Architecture Structure

classDiagram
    direction TB

    class HTMLElement {
        <<browser>>
    }

    class HICPageflip {
        +canvas: HTMLCanvasElement
        +pageflip: Pageflip
        +engineMode: string
        +flipForward()
        +flipBackward()
        +gotoPage(n)
        +switchEngine(mode)
    }

    class HICPageflipPage {
        +pageNum: number
    }

    class Pageflip {
        +engine: BaseEngine
        +activeFlip: object
        +currentPage: number
        +currentSpread: number[]
        +handlePaint()
        +startCornerPeek(corner)
        +startDrag(x, y)
        +flipForward()
        +flipBackward()
        +render()
    }

    class BaseEngine {
        <<abstract>>
        +canvas: HTMLCanvasElement
        +slides: object[]
        +render(state)*
        +resize()*
        +destroy()*
    }

    class HICPageflipEngine2D {
        +ctx: CanvasRenderingContext2D
        +renderPages(ctx, state)
        +clipHalfPlane(ctx, fold, onDragSide)
        +drawFoldUnderShadow()
        +warmupSlidePaintRecords()
    }

    class HICPageflipEngine3D {
        +gl: WebGLRenderingContext
        +program: WebGLProgram
        +meshBuffers: object
        +drawTurningSheet(front, back, side, flip)
        +drawDoubleSidedPage(front, back, side, isActive, cyl)
        +rasterizeSlideToTexture(slide)
    }

    HTMLElement <|-- HICPageflip
    HTMLElement <|-- HICPageflipPage
    HICPageflip *-- Pageflip : contains
    HICPageflip o-- HICPageflipPage : slots light DOM
    Pageflip *-- BaseEngine : delegates rendering to
    BaseEngine <|-- HICPageflipEngine2D : implements
    BaseEngine <|-- HICPageflipEngine3D : implements

Architectural Highlights

  1. Shadow DOM & Layout Subtree: <hic-pageflip> encapsulates the rendering canvas in its Shadow Root using <canvas layoutsubtree><slot></slot></canvas>. This projects light-DOM <hic-pageflip-page> elements directly into the browser's canvas layout subtree without manual DOM relocation.
  2. Engine Decoupling: Pageflip acts as an engine-agnostic controller managing gesture coordinates, timeline animations, paper constraint math (constrainPaper), and page state. It delegates painting to whichever BaseEngine implementation is currently active.
  3. Pluggable Engine Hierarchy: Both HICPageflipEngine2D and HICPageflipEngine3D extend BaseEngine and implement a unified interface (render, resize, setDimensions, destroy), allowing seamless hot-switching between 2D and 3D rendering modes on the fly.

Development

Run Locally

# Start local development server on port 3000
npm start

Point your browser at http://localhost:3000 to see.

Deploy the demo to Netlify

npm run deploy

Publish the package

Do not run npm publish but, instead, run a custom publish script:

npm run pub

The script will automatically build the project first into the dist folder, and only publish the contents of that folder.


References


License

MIT © Bramus Van Damme