Folio3D
A 3D flipbook viewer for PDF and image books. Written in plain JavaScript, with no frameworks and no jQuery. This page covers the JavaScript library.
Quick start
Copy dist/folio3d.min.js and dist/folio3d.min.css to your site, then:
<link rel="stylesheet" href="folio3d.min.css">
<script src="folio3d.min.js"></script>
<div id="book" style="height: 640px"></div>
<script>
const book = new Folio3D('#book', {
pdf: 'files/catalog.pdf',
title: 'Spring Catalog',
});
</script>
For a book made of images, pass pages instead of pdf:
new Folio3D('#book', {
pages: [
'pages/01.jpg',
{ src: 'pages/02.jpg', thumb: 'pages/02-small.jpg', caption: 'Contents' },
{ src: 'pages/03.jpg', text: 'Text of the page, used for search and reader mode' },
],
});
As an ES module:
import Folio3D from './folio3d.esm.js';
No-code embed
Elements with these attributes are initialised automatically when the page loads.
<!-- inline viewer: a PDF URL or a JSON options object -->
<div data-folio3d="catalog.pdf" style="height: 70vh"></div>
<div data-folio3d='{"pdf": "catalog.pdf", "theme": "dark"}' style="height: 70vh"></div>
<!-- opens in a lightbox on click -->
<a href="catalog.pdf" data-folio3d-lightbox data-title="Catalog">Read the catalog</a>
To disable auto-initialisation, set window.Folio3DAutoInit = false before the script loads. Content added later can be initialised with Folio3D.autoInit(container).
Options
| Option | Default | Description |
|---|---|---|
pdf | null | PDF URL, ArrayBuffer or Uint8Array. |
pages | null | Array of image URLs or {src, thumb, caption, text} objects. |
id | null | Unique id. Used for bookmarks, “continue reading” and the address bar hash. |
title | '' | Shown in the top bar and used for sharing and accessibility. |
mode | 'auto' | 'webgl' (realistic curl), 'css3d', 'swipe' or 'auto'. If WebGL is unavailable, the viewer falls back to CSS 3D. |
layout | 'auto' | 'double', 'single' or 'auto', which chooses by container shape. |
singleCover | true | Show the first page alone, like a book cover. |
centerSingle | true | Center the closed book (cover or back page). |
startPage | 1 | Page to open at. |
rtl | false | Right-to-left books (Arabic, Hebrew, manga). |
theme | 'auto' | 'light', 'dark' or 'auto' (follows the visitor’s system). |
accent | null | Accent color, for example '#0f766e'. |
colors | null | Override design tokens, for example {stage: '#111', paper: '#fffdf8'}. See Theming. |
height | null | Sets the container height, for example 640 or '80vh'. |
flipDuration | 900 | Page turn duration in ms. Visitors who prefer reduced motion get instant turns. |
sound, soundVolume, soundUrl | true, 0.5, null | Page turn sound. It is synthesized by default; set soundUrl to use your own file. |
autoplay, autoplayInterval, autoplayLoop | false, 4000, true | Turn pages automatically. |
toolbar, arrows, progressBar | true | Show the toolbar, the side arrows and the reading progress bar. |
thumbnails, toc, search, bookmarks | true | Side panel tabs. toc also accepts an array: [{title, page, items}]. |
share, download, print, fullscreen, readerMode | true | Toolbar and menu actions. |
autoplayButton, soundButton, modeSwitch, themeSwitch | true | Items in the “more” menu. |
downloadUrl | null | File offered by the download button. Defaults to the PDF URL. |
captions | null | Array (by page) or object {pageNumber: text} shown under the book. |
hotspots | [] | Interactive areas. See Hotspots. |
pdfLinks | true | Make links inside the PDF clickable. |
deepLink | true | Keep the page in the URL hash (#page=5, or #book-ID=5 when id is set). |
resume | true | Offer to continue from the last page read. |
zoomMax, zoomStep | 4, 0.5 | Zoom limits. |
clickToFlip | true | Click on a page side to turn it. |
wheelFlip | true | Mouse wheel turns pages in fullscreen and the lightbox. |
lang, strings | 'auto', null | Interface language and string overrides. See Languages. |
pdfjsSrc, pdfjsWorkerSrc | cdnjs | Where to load pdf.js from. Point these at your own copy to self-host it. |
pdfPassword | null | Password of a protected PDF. |
crossOrigin | 'anonymous' | CORS mode for page images. |
onReady, onPagechange, … | Callbacks for every event (on + capitalised event name). |
Methods
const book = new Folio3D('#book', options);
await book.ready; // resolves when the first spread is shown
book.next(); book.prev(); // reading direction (respects rtl)
book.flip(1); book.flip(-1); // visual direction: right / left
book.goToPage(12); // 1-based
book.first(); book.last();
book.setZoom(2); book.zoomIn(); book.zoomOut();
book.setMode('css3d'); // 'webgl' | 'css3d' | 'swipe'
book.setLayout('single'); // 'single' | 'double' | 'auto'
book.setTheme('dark'); // 'light' | 'dark' | 'auto'
book.toggleFullscreen();
book.play(); book.pause();
const results = await book.search('price'); // [{page, snippet}]
book.setHighlight('price'); // '' clears
book.toggleBookmark(5); book.getBookmarks();
book.share(); book.download(); book.printPage(); book.openReader();
book.currentPage; book.visiblePages; book.pageCount; book.mode;
book.destroy();
Folio3D.open(options); // lightbox, returns the viewer
Folio3D.autoInit(container); // initialise data attributes in a container
Events
The viewer is an EventTarget. Event data is in event.detail.
book.addEventListener('pagechange', (e) => console.log(e.detail.page));
book.on('ready', ({ pages }) => console.log(pages + ' pages')); // shorthand, chainable
| Event | Detail |
|---|---|
ready | {pages} |
pagechange | {page, pages, total} |
zoom | {zoom} |
modechange | {mode} |
themechange | {theme} |
autoplay | {playing} |
bookmarks | {bookmarks} |
download | {url} |
error | {error}: the book could not be loaded. |
destroy | {} |
Hotspots & links
Links inside PDFs work automatically. Hotspots add interactive areas to any page. Coordinates are fractions of the page size, from 0 to 1.
hotspots: [
{ page: 3, type: 'video', url: 'https://www.youtube.com/watch?v=VIDEO_ID', x: .1, y: .4, w: .8, h: .3, title: 'Watch' },
{ page: 5, type: 'link', url: 'https://example.com/product', x: .6, y: .1, w: .3, h: .2 },
{ page: 2, type: 'page', target: 12, x: .05, y: .3, w: .9, h: .08 },
{ page: 7, type: 'iframe', url: 'https://example.com/map', x: 0, y: .5, w: 1, h: .5 },
{ page: 9, type: 'html', html: '<button class="buy">Buy now</button>', x: .1, y: .8, w: .3, h: .08 },
]
Video hotspots accept YouTube, Vimeo or a direct video file. YouTube uses the privacy-friendly youtube-nocookie.com player.
Theming
Every color, radius and blur is a CSS custom property on .f3d. Override them in CSS or with the colors option, where keys drop the --f3d- prefix. The theme builder demo (packages/core/demo/builder.html) generates both.
.f3d {
--f3d-accent: #0f766e; /* buttons, progress, highlights */
--f3d-stage: #eef1f4; /* background (any CSS background) */
--f3d-surface: rgba(255,255,255,.78); /* toolbar, panels (glass) */
--f3d-text: #161a23;
--f3d-muted: #646b7a;
--f3d-paper: #ffffff; /* blank page color */
--f3d-radius: 16px;
--f3d-blur: 18px;
--f3d-font: 'Inter', sans-serif;
}
Dark values live in .f3d[data-theme="dark"], and the same values apply automatically for theme: 'auto' when the system is dark.
Languages
English and Russian are included. The language is picked from lang, or from the page’s <html lang> when lang is 'auto'. To translate, pass the strings that differ from English:
new Folio3D('#book', { pdf: 'a.pdf', lang: 'de', strings: { next: 'Weiter', prev: 'Zurück', search: 'Suche' } });
All keys are in src/i18n/strings.js.
Keyboard
← → turn pages · PgUp PgDn previous/next · Home End first/last · + − 0 zoom · F fullscreen · Esc closes panels, resets zoom and closes the lightbox. On touch screens: swipe to turn pages and pinch to zoom.
Using WordPress?
Folio3D is also available as a separate WordPress plugin from Codestan, with an admin UI, a block and shortcodes.
Building from source
The sources in src/ are ES modules that run directly in the browser, so no build is needed to develop. To rebuild the distribution files (Node 18+, no npm packages), run in the library folder:
node build.mjs # rebuild dist/ from src/
node serve.mjs # http://localhost:5173/ → demos
Browser support
Current Chrome, Edge, Firefox and Safari, on desktop, iOS 15+ and Android. WebGL mode falls back to CSS 3D when WebGL is unavailable. On iPhone, where the Fullscreen API is not available for elements, fullscreen fills the window instead.
FAQ
The PDF does not load
Open the browser console. Usually the PDF is on another domain without CORS headers, or the URL is wrong. Upload the PDF to the same site, or enable CORS on the file server.
Pages look blurry when zoomed
Pages are re-rendered at a higher resolution shortly after zooming stops. With image books, upload images at least 1600 px wide.
Can I use it with React, Vue or another framework?
Yes. Create the viewer in a mount hook and call destroy() on unmount. It has no dependencies of its own.
Credits
- pdf.js by Mozilla, Apache License 2.0. It is loaded at runtime only for PDF books and is not bundled into
folio3d.js. - All other code, icons, the page-turn sound (synthesized) and the sample pages are original to Folio3D.