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 🙏

© 2025 – Pkg Stats / Ryan Hefner

lenti

v0.3.2

Published

Lenticular image viewer

Downloads

30

Readme

lenti v0.3.2


Lenti is an image viewer that mimicks the effect of lenticular printing. It displays images in a canvas element and binds events for mouse and accelerometer events, so just as you would rotate a card or print with lenticular lenses on it, you can tilt your phone to transition between images.

Demo

Installation

npm install --save lenti

Usage

Lenti will accomodate any number of images in the container.

<div data-lenticular-list="true">
  <img src="./images/sample_a_1.png" alt="Left-facing view of object" width="1280" height="720" />
  <img src="./images/sample_a_2.png" alt="Center-left-facing view of object" width="1280" height="720" />
  <img src="./images/sample_a_3.png" alt="Center-facing view of object" width="1280" height="720" />
  <img src="./images/sample_a_4.png" alt="Center-right-facing view of object" width="1280" height="720" />
  <img src="./images/sample_a_5.png" alt="Right-facing view of object" width="1280" height="720" />
</div>
import {Lenti, bindGyroscopeXY, bindMouseXY} from "lenti"

const container = document.querySelector("[data-lenticular-list]")
const canvas = document.createElement("canvas") // programmatically creating canvas. could also just put it in the HTML
container.appendChild(canvas)
const lenti = new Lenti({
  container,
  canvas,
  images: Array.from(container.querySelectorAll("img")),
  uiAdapters: [bindMouseXY({eventRoot: window}), bindGyroscopeXY()]
})

Browser Support

Lenti relies on WebGPU for buttery-smooth framerates on modern computers. Unflagged browser support below:

Thanks to Ire for their Can I Use Embed

API

Lenti

Lenti

TODOs:

  • [ ] Add support for pivotted x/y values
  • [ ] Add support for touch events
  • [ ] MSAA instead of just rendering higher resolutions https://webgpufundamentals.org/webgpu/lessons/webgpu-multisampling.html#a-multisampling

Constructors

new Lenti()
new Lenti(options: {
  canvas: HTMLCanvasElement;
  images: HTMLImageElement[];
  oversampling: number;
  samplerSettings: GPUSamplerDescriptor;
  uiAdapters: UIAdapter[];
 } & Partial<{
  lensDarkening: number;
  lensDistortion: number;
  stripWidth: number;
  transition: number;
  viewX: number;
  viewY: number;
 }>): Lenti
Parameters
options

{ canvas: HTMLCanvasElement; images: HTMLImageElement[]; oversampling: number; samplerSettings: GPUSamplerDescriptor; uiAdapters: UIAdapter[]; } & Partial<{ lensDarkening: number; lensDistortion: number; stripWidth: number; transition: number; viewX: number; viewY: number; }>

Returns

Lenti

Properties

| Property | Type | Default value | Description | | ------ | ------ | ------ | ------ | | canvas | HTMLCanvasElement | null | The output (rendered) canvas | | images | ( | HTMLCanvasElement | HTMLOrSVGImageElement | HTMLVideoElement | ImageBitmap | OffscreenCanvas | ImageData)[] | [] | Image elements to pull textures from. Also supports ImageData. | | inputs | { lensDarkening: number; lensDistortion: number; stripWidth: number; transition: number; viewX: number; viewY: number; } | undefined | - | | inputs.lensDarkening | number | undefined | Amount of darkening to apply near the virtual off-axis parts of the lenticule | | inputs.lensDistortion | number | undefined | Amount of y-axis distortion applied to the lenticule simulate vertical off-axis viewing | | inputs.stripWidth | number | undefined | Image-space width of the strip placed in an interlaced array under the lenticule | | inputs.transition | number | undefined | Amount of virtual warping to apply to the transition from left–right | | inputs.viewX | number | undefined | [0: Leftmost image, 1: Rightmost image] | | inputs.viewY | number | undefined | [0: Top distortion, 1: Bottom distortion] | | isVisible | boolean | false | Whether the canvas is visible in the viewport | | oversampling | number | 2 | Canvas oversampling | | uiAdapterCleanupCallbacks | UIAdapterCleanupCallback[] | [] | - | | uiAdapters | UIAdapter[] | undefined | UI adapters connect user input to the shader settings, custom adapters can be made Default [bindMouseXY(), bindGyroscopeXY()] |

Accessors

imageAspectRatio
Get Signature
get imageAspectRatio(): number
Returns

number

Methods

createTextureFromImage()
createTextureFromImage(imageData: ImageData): Promise<GPUTexture>
Parameters
imageData

ImageData

Returns

Promise<GPUTexture>


destroy()
destroy(): void

Destroys the instance and cleans up resources

Returns

void


error()
error(e: Error): void

Common Lenti errors may be fired from here

Parameters
e

Error

Returns

void


init()
init(): Promise<void>
Returns

Promise<void>


render()
render(): void

Renders the current state of the instance

Returns

void


update()
update(updates: Partial<{
  lensDarkening: number;
  lensDistortion: number;
  stripWidth: number;
  transition: number;
  viewX: number;
  viewY: number;
 }>): void

Updates the instance state with a subset of Lenti's state

Parameters
updates

Partial<{ lensDarkening: number; lensDistortion: number; stripWidth: number; transition: number; viewX: number; viewY: number; }>

Returns

void


bindGyroscopeXYOptions

Properties

| Property | Type | Description | | ------ | ------ | ------ | | userGestureElement? | HTMLElement | Some browsers require user gesture before requesting permission. This is the element that will require click if so. By default, this is the Lenti instance's canvas element, but it can be a button or other interactive element. | | xBounds | [number, number] | Range of angles in object-space x-rotation | | yBounds | [number, number] | Range of angles in object-space y-rotation |


Degree

type Degree = number;

A number representing degrees (typically [-360, 360])


NormalizedNumber

type NormalizedNumber = number;

A number in the range [0, 1]


PositiveInteger

type PositiveInteger = number;

A positive integer

Managing User Interaction

UIAdapter()

type UIAdapter = (lentiInstance: Lenti) => UIAdapterCleanupCallback | void;

UI Adapaters listen for events on a page and can access Lenti properties throughout the instance lifecycle.

UIAdapters expect a Lenti instance to be passed to them, and can be used to bind user input to shader settings.

In practice, this can be as simple as:

// Sets the lensDarkening based on the amount of daylight when the page loads
new Lenti({uiAdapters: [function bindDaylight(lentiInstance: Lenti) => {
  lentiInstance.inputs.lensDarkening = 0.5
})]})

Parameters

lentiInstance

Lenti

Returns

UIAdapterCleanupCallback | void


UIAdapterCleanupCallback()

type UIAdapterCleanupCallback = () => void;

UIAdapterCleanupCallback is a function that is called when a Lenti instance is destroyed.

Some cases might be usage in a single-page application where the Lenti instance is created and destroyed multiple times as a user navigates the site.

Returns

void


UIAdapterFactory()

type UIAdapterFactory = (options?: any) => UIAdapter;

UIAdapterFactory is an initializing function that is passed options for the UIAdapater it contains.

If you want your UIAdapter to be reusable and configurable, you can create a UIAdapterFactory that returns the UIAdapter.

// Sets the lensDarkening based on the amount of daylight when the page loads
const bindDaylightFactory = (options: {daylight: number}) => {
  return function bindDaylight(lentiInstance: Lenti) => {
    lentiInstance.inputs.lensDarkening = 1 - options.daylight
  }
}

new Lenti({uiAdapters: [bindDaylightFactory({daylight: 0.5})]})

Parameters

options?

any

Returns

UIAdapter


bindGyroscopeXY()

function bindGyroscopeXY(options: bindGyroscopeXYOptions): UIAdapter

Drives viewX/viewY based on the device viewing angle

Parameters

options

bindGyroscopeXYOptions = ...

Returns

UIAdapter


bindMouseXY()

function bindMouseXY(options: {
  eventRoot:   | HTMLElement
     | Window
     | Document;
 }): UIAdapter

Drives viewX/viewY based on the mouse position on the element, in the browser window, or in another element (like a touchstrip element)

Parameters

options
eventRoot

| HTMLElement | Window | Document

The element that the mouse will be tracked on

Returns

UIAdapter