Skip to content
Lenticular image viewer
Branch: master
Clone or download
Latest commit 7a10057 Mar 10, 2017
Type Name Latest commit message Commit time
Failed to load latest commit information.
src tempCtx should ref tempCanvas Mar 10, 2017
.babelrc babel Mar 6, 2017
.editorconfig packaging up for publishing Mar 5, 2017
.gitattributes packaging up for publishing Mar 5, 2017
.gitignore babel Mar 6, 2017
.npmignore cleanup Mar 6, 2017
.travis.yml remove v4 for now since it can't figure out babel-jest Mar 9, 2017
.yo-rc.json re-initing package Mar 6, 2017
LICENSE packaging up for publishing Mar 5, 2017 adding instance method docs Mar 10, 2017
package.json 0.2.1 Mar 10, 2017

lenti NPM version Build Status Dependency Status Coverage percentage

Lenticular image viewer

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.



$ npm install --save lenti

Basic Usage

Lenti will accomodate any number of images in the container (be good to your RAM and don’t go wild, though).

<div data-lenticular-list="true" >
  <img src="assets/images/1.jpg" alt="Blue Image" width="1280" height="720" />
  <img src="assets/images/2.jpg" alt="Blue Image" width="1280" height="720" />
  <img src="assets/images/3.jpg" alt="Blue Image" width="1280" height="720" />
  <img src="assets/images/4.jpg" alt="Blue Image" width="1280" height="720" />
import Lenti from 'lenti'

let lenticulars = document.querySelectorAll('[data-lenticular-list]')
let instances = []
// convert → array & loop through
;[...lenticulars].map((el, i) => {
  // store instance in array for further manipulation
  instances[i] = new Lenti({container: el, width: 1280, height: 720})
  // initialize instance




Specifies the HTMLElement (not selector) that contains the images.


default: true

Turns tilt interaction on or off.


default: true

Turns mouse hover interaction on or off.


default: 16

The horizontal width (in pixels) of each lens strip.

height & width

default: 50

The height and width of the canvas (in pixels). You definitely should match this to the value of your images (which should all be the same size)


default: 45


default: -45

For the accelerometer event listener, define the max and min tiltable angle for interaction.

Instance Methods


Runs setup functions.



Binds (bindEvents) or unbinds (destroy) events.


Measures the sizing of the box for further calculations. By default the event bindings will call this on resize. If you are resizing a container manually, you should probably fire this.





Refreshes the viewer for the given balance, where balance is a float from 0–1 that represents the position in the image sequence. A value of 0 will show the first image, and a value of 1 will show the last. See custom events for an example of this.

Lenti.remap(value, inLow, inHigh, outLow, outHigh)

Helper function to map values from one range to another. You'll likely use this to map values to the range 0–1 for Lenti.redraw().

Custom events

Lenti doesn't make too many assumptions about your environment. You may turn off the default event handlers (see accelerometerEvents and mouseEvents) and make your own interaction system. Just send a value between 0–1 to your instance at Lenti.redraw().

In the following example, we show how a spring physics library (rebound) can be used as a sort of middleware in Lenti:

import Lenti from 'lenti'
import rebound from 'rebound'

let lenticulars = document.querySelectorAll('[data-lenticular-list]')
let instances = []
// convert → array & loop through
;[...lenticulars].map((el, i) => {
	const image = el.querySelector('img')
	// store instance in array for further manipulation
	instances[i] = new Lenti({
		container: el,
		width: image.width,
		height: image.height,
		stripWidth: el.getAttribute('data-strip-width'),
		mouseEvents: false // this is the key
	let _this = instances[i]

	// set up spring
	const springSystem = new rebound.SpringSystem();
	const springConfig = [40, 9] // tension, friction
	const balanceSpring = springSystem.createSpring(...springConfig);
	balanceSpring.addListener({ onSpringUpdate: (balanceSpring) => {
	// initialize instance

	// set initial value

	// bind mouse events
	_this.canvas.addEventListener('mousemove', (e) => {
		const balance = _this.remap(e.offsetX / _this.canvasWidth, 0, 1, 1, 0)

After disabling the default mouse event handler, we set up a new event listener (mousemove), map the value of e.offsetX / _this.canvasWidth to the range 1–0 (just inverting the range here), and send the value to balanceSpring, which interpolates the value. We tell balanceSpring to send the spring value to our instance method at Lenti.redraw() as it updates. Check the demo page to see this example in use.

You can imagine that this example does not demonstrate the full flexibility you have here, and that you could, for instance, replace the default gamma-rotation accelerometer event with events for a different axis, use ambient light sensors to change the value, have timed animations driven by any arbitrary event, and so on.

Cross-origin images

Because Lenti uses canvas to produce this effect, most browsers will be upset if you fetch an image from another origin. Be sure to set crossorigin="anonymous" on your images:

<img src="" alt="Blue Image" crossorigin="anonymous" width="1280" height="720" />


Apache-2.0 © Daniel Gamage

You can’t perform that action at this time.