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

bookcover-3d-web

v0.5.0

Published

Create 3D images of books by providing the book cover designs. Part of the [bookcover](https://github.com/gracious-tech/bookcover) generation system.

Readme

bookcover-3d-web

Create 3D images of books by providing the book cover designs. Part of the bookcover generation system.

npm install bookcover-3d-web

Example 3D image

This is a WebGL 3D book renderer with no external graphics dependencies.

import {Book3DRenderer, generate} from 'bookcover-3d-web'
import type {BookFaces, CoverType, GenerateOptions} from 'bookcover-3d-web'

Both exports take the same SVG face inputs and produce the same kind of output. The difference is lifecycle: generate is a one-shot helper that creates and destroys a renderer internally, while Book3DRenderer keeps the WebGL context alive so you can re-render cheaply (e.g. on mouse drag) without reloading textures.

Use generate for static exports. Use Book3DRenderer for interactive previews.

generate(svgs, options?): Promise<Uint8Array>

Creates a renderer, loads the SVGs, renders once, returns PNG bytes, then cleans up.

import {generate} from 'bookcover-3d-web'

const png = await generate(
    {front: frontSvg, back: backSvg, spine: spineSvg},
    {cover_type: 'paperback', azimuth: -30, width: 800, height: 600},
)
interface BookFaces {
    front: string   // SVG string
    back: string    // SVG string
    spine?: string  // SVG string (optional — depth_mm used if absent)
}

interface GenerateOptions {
    cover_type?: 'paperback'|'paperback_coil'|'paperback_wire'|'paperback_stitch'|'hardcover'|'hardcover_jacket'
    azimuth?: number     // horizontal camera angle in degrees (default: -30)
    elevation?: number   // vertical camera angle in degrees (default: 20)
    roll?: number        // clockwise rotation in degrees (default: 0)
    width?: number       // canvas width in pixels (default: 800)
    height?: number      // canvas height in pixels (default: 600)
}

Book3DRenderer

Persistent renderer for interactive use. Load SVGs once; call render() repeatedly with different angles without reloading textures each time.

import {Book3DRenderer} from 'bookcover-3d-web'

// Create once
const renderer = new Book3DRenderer(800, 600)

// Load SVGs and build geometry (do this when the cover changes)
await renderer.load({front: frontSvg, back: backSvg, spine: spineSvg}, 'paperback')

// Render at any angle — fast, no texture reload
renderer.render(-30, 20)   // azimuth, elevation

// Export the current frame
const png = await renderer.to_png()

// Free GPU resources when done
renderer.destroy()

Full API

const renderer = new Book3DRenderer(width?, height?)

// Load cover face SVGs and build 3D geometry
await renderer.load(svgs: BookFaces, cover_type?: CoverType, depth_mm?: number)

// Render at camera angles (all in degrees)
renderer.render(azimuth?, elevation?, zoom?, roll?, light_az?, light_el?, ambient?, exposure?)

// Resize without recreating WebGL context
renderer.resize(width, height)

// Get projected width/height ratio at default viewing angle
renderer.get_projected_aspect(): number

// Composite onto a background photo
await renderer.composite_photo(background: ImageBitmap, options?: PhotoCompositeOptions)

// Export current frame
await renderer.snapshot(): Promise<ImageBitmap>
await renderer.to_png(): Promise<Uint8Array>

// Free GPU resources
renderer.destroy()

Photo compositing

composite_photo renders the book at a certain angle and composites it over a background image, with a shadow derived from the light direction. It returns an ImageBitmap at the background's native resolution.

Example photo

The library describes a set of background photos via BACKGROUNDS (metadata only — the JPGs themselves are not bundled in the npm package; they live in the repo's assets tree for you to host alongside your other static assets). Each entry has an id (to identify which image to load) and pre-tuned camera/lighting options that make the book look natural in that scene. Pass the background entry directly as the options argument — it extends PhotoCompositeOptions.

import {Book3DRenderer, BACKGROUNDS} from 'bookcover-3d-web'
import type {Background} from 'bookcover-3d-web'

// Find the background you want
const bg: Background = BACKGROUNDS.find(b => b.id === 'coffee_table')!

// Load the image however you serve your assets
const img = await fetch(`/assets/3d/backgrounds/${bg.id}.jpg`)
const blob = await img.blob()
const bitmap = await createImageBitmap(blob)

// Render the book, then composite — pass the background entry as options
await renderer.load({front: frontSvg, back: backSvg, spine: spineSvg})
const result = await renderer.composite_photo(bitmap, bg)

// result is an ImageBitmap at the background's native resolution

Available background IDs: table_with_book, wood, coffee_table, table_with_laptop, table_side.

You can also composite onto your own image by passing a plain PhotoCompositeOptions object (all fields optional — defaults give a flat overhead perspective):

interface PhotoCompositeOptions {
    azimuth?: number      // horizontal camera angle in degrees (default: -15)
    elevation?: number    // vertical camera angle in degrees (default: 35)
    zoom?: number
    roll?: number         // clockwise rotation in degrees (default: 0)
    book_scale?: number   // book width as fraction of background width (default: 0.55)
    offset_x?: number     // position offset as fraction of background width (0 = centred)
    offset_y?: number     // position offset as fraction of background height (0 = centred)
    light_az?: number     // light horizontal angle in degrees
    light_el?: number     // light vertical angle in degrees
    ambient?: number      // ambient light level 0–1
}