JavaScript Documentation
Setup, media, settings and API reference for Filmstrip Photo & Video Slider.
Getting started
Filmstrip Photo & Video Slider displays a draggable 3D ribbon. A frame opens a category of photos and looping videos. Categories stay inside the same Three.js canvas, with their own navigation, captions and optional category selector.
- Serve the page over HTTP or HTTPS. Opening an HTML file directly with file:// will not load the model and textures correctly.
- Use a browser with WebGL support. The gallery creates a WebGL canvas for its media and HTML controls for navigation.
- Use native JavaScript booleans such as
trueandfalse. Do not quote them. - All public duration and delay settings use seconds. For example,
0.65means 650 milliseconds. - Scene dimensions, CSS pixel values and progress fractions have different units. Each setting below states which unit it uses.
Installation
Start with start.html, the complete customer reference. It contains the current settings and the gallery HTML for the supplied photo and video collections. Keep it beside the src, content and media folders.
| Path beside start.html | Purpose |
|---|---|
start.html | Editable customer page with the visible gallery host, hidden category markup and constructor settings. |
src/FWDFPAVS.js | Compiled script that exposes the FWDFPAVS constructor. Three.js and the model decoder are bundled with it. |
content/global.css | Gallery controls, dropdown, preloader and error styles. |
content/ | Keep the complete folder, including the textures used by optional effects. |
media/models/film_frame.glb | Supplied film holder model. |
media/images/ and media/videos/ | Sample photos, videos and posters. Replace these and update the HTML paths. |
Upload the page and its folders to your server, then open start.html over HTTP or HTTPS. A customer using the compiled files does not need Node.js or a build step. Keep filename capitalization unchanged on servers with case-sensitive paths.
Load the stylesheet in the document head and the script before calling the constructor. Create the visible host and hidden data markup first. Load your chosen fonts before initialization so film labels and captions use the correct measurements.
<link rel="stylesheet" href="content/global.css">
<script src="src/FWDFPAVS.js"></script>In the source project, edit public/start.html and run npm run build. The command refreshes public/src/FWDFPAVS.js, then copies the reference and its assets to build/start.html. Publish the contents of build. Do not make permanent edits only in the generated build folder.
The marketing demos use a separate layout with js/filmstrip-gallery.js, content/filmstrip.css and media/gallery/. Those paths are for the demo pages. Use the paths in start.html for the customer reference and do not load both bundles or both stylesheets.
Keep the supplied model unless you have adapted a replacement to the expected holder and photo geometry. Pointing the setting at an arbitrary GLB is not enough to make it a compatible film frame.
Editing start.html
Use this file as your first integration reference. It includes landscape and portrait images, video covers, mixed photo and video categories, and optional captions.
- The visible
div id="myDiv"is the gallery host. The page usesdisplayType: 'afterparent', so the CSS height of this host controls the gallery height. Change its height to fit your layout. - The hidden
div id="carouselData"holds one article per category. Edit the cover, title, film code and media figures there. Keep at least two ribbon articles and give each a unique category ID. - The settings are grouped at the bottom of the file. Change those values before initialization. Use native booleans and seconds for durations. Keep parentId and carouselDataId matched to the HTML IDs.
- The final script loads
src/FWDFPAVS.js, waits for Roboto, and callsnew FWDFPAVS(...)once. It stores the controller aswindow.galleryfor API calls. - Replace each photo, video and poster path with your own media. Update width and height to the real dimensions. Remove caption attributes from items that should have no caption.
- For a bounded gallery inside a scrolling page, remove the full-page overflow rule and give the host your desired height. Alternatively use responsive mode with maxHeight and autoScale as described below.
gallery.getState()
gallery.openCategory(0)
gallery.destroy()The customer reference and the source development page index.html are separate editable pages. A build preserves your edits to public/start.html while refreshing its compiled script.
Compact demo example
For a shorter illustration, this separate example uses two categories and the marketing demo asset paths. Save it beside the demo index page. For customer installation, start with start.html and the paths listed above.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Filmstrip quick start</title>
<link rel="stylesheet" href="content/filmstrip.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;600;700&display=swap">
<style>
body { margin: 0; background: #fff; font-family: Roboto, sans-serif; }
#gallery-holder { width: 100%; height: 100vh; height: 100dvh; }
</style>
</head>
<body>
<div id="gallery-holder"></div>
<div id="gallery-data" hidden>
<article data-gallery-id="mountains" data-title="ALPINE LIGHT" data-film-code="01 / 02">
<div data-src="media/gallery/videos/138276.mp4" data-poster="media/gallery/videos/138276.webp" data-width="1280" data-height="720"></div>
<template data-gallery>
<h2 data-gallery-title>Alpine Light</h2>
<p data-gallery-category>Alpine Light</p>
<figure data-gallery-media>
<video data-gallery-src="media/gallery/videos/138276.mp4" poster="media/gallery/videos/138276.webp" width="1280" height="720" aria-label="Clouds drifting over a sunlit alpine mountain valley." data-caption-title="Alpine Air" data-caption-description="Clouds move across the ridgeline." muted loop playsinline preload="none"></video>
</figure>
<figure data-gallery-image>
<img data-gallery-src="media/gallery/images/collections/mountains/8195019.webp" data-caption-title="A Clear Reflection" data-caption-description="Blue sky, still water and a mountain reflected." width="1280" height="853" alt="The Matterhorn reflected in a bright blue alpine lake beside rocks and green grass.">
</figure>
</template>
</article>
<article data-gallery-id="waterfalls" data-title="FALLING WATER" data-film-code="02 / 02">
<div data-src="media/gallery/videos/267095.mp4" data-poster="media/gallery/videos/267095.webp" data-width="720" data-height="1280"></div>
<template data-gallery>
<h2 data-gallery-title>Falling Water</h2>
<p data-gallery-category>Falling Water</p>
<figure data-gallery-image>
<img data-gallery-src="media/gallery/images/collections/waterfalls/272654.webp" data-caption-title="Pools of Jade" data-caption-description="Water steps through the green canopy." width="1280" height="857" alt="Terraced waterfalls flow into bright turquoise pools surrounded by vivid green foliage.">
</figure>
<figure data-gallery-image>
<img data-gallery-src="media/gallery/images/collections/waterfalls/2191156.webp" width="960" height="1280" alt="Portrait view from above of a waterfall dropping into a turquoise pool beneath leafy trees.">
</figure>
</template>
</article>
</div>
<script src="js/filmstrip-gallery.js"></script>
<script>
async function startGallery() {
if (document.fonts) await document.fonts.ready
window.gallery = new FWDFPAVS({
instance: 'exampleGallery',
parentId: 'gallery-holder',
carouselDataId: 'gallery-data',
displayType: 'afterparent',
backgroundColor: '#ffffff',
filmFrameModel: 'media/gallery/models/film_frame.glb',
frameWidth: 6.8,
frameHeight: 5.1,
filmStripColor: '#000000',
filmStripTextColor: '#d9d9d9',
showPreloader: true,
preloaderImageDelay: 0.05,
useBlackAndWhite: true,
opacityStrength: 1,
imageSharpenStrength: 0.5,
useFilmCurve: true,
useRibbonMouseWheel: false,
useCategoryMouseWheel: false,
useCategoryPickerMouseWheel: true,
categoryButtonColor: '#111111',
categoryButtonIconColor: '#ffffff',
categoryTextColor: '#111111',
categoryImageCounterColor: '#707070',
categoryCurrentImageNumberColor: '#000000',
showCateogryNavigationArrows: true,
showCateogryNavigationSelect: true,
useFlowmap: false,
useNoise: false,
grid: false,
waveAmplitude: 0,
deepLinking: true,
stats: false,
gui: false,
events: {
ready(event) {
console.log('Gallery ready', event.categories)
},
categoryOpened(event) {
console.log('Category opened', event.category.title)
},
error(event) {
console.error(event.message)
}
}
})
}
startGallery()
</script>
</body>
</html>Categories, images and videos
Add one article for each ribbon entry inside the hidden data element. A category article needs a unique data-gallery-id, a cover element, and a template data-gallery. The template keeps its media unloaded until the gallery requests it. The slider reads this markup, it does not scan folders on your server.
Use at least two ribbon entries. The current parser keeps the last 49 entries if more than 49 are supplied. Each category needs at least one media figure and a nonempty gallery title.
<div id="gallery-data" hidden>
<article data-gallery-id="coast" data-title="SALT AND SEA" data-film-code="01 / 02">
<div data-src="media/coast/cover.webp" data-width="1280" data-height="853"></div>
<template data-gallery>
<h2 data-gallery-title>Salt and Sea</h2>
<figure data-gallery-media>
<img data-gallery-src="media/coast/cover.webp" width="1280" height="853"
alt="Sunlight over the coast" data-deep-link-id="open-blue"
data-caption-title="An Open Blue"
data-caption-description="A quiet morning by the water.">
</figure>
<figure data-gallery-media>
<img data-gallery-src="media/coast/portrait.webp" width="853" height="1280"
alt="A tall cliff beside the sea">
</figure>
</template>
</article>
<article data-gallery-id="woods" data-title="DEEP WOODS" data-film-code="02 / 02">
<div data-src="media/woods/stream.mp4" data-poster="media/woods/stream.webp"
data-width="720" data-height="1280"></div>
<template data-gallery>
<h2 data-gallery-title>Deep Woods</h2>
<figure data-gallery-media>
<video data-gallery-src="media/woods/stream.mp4" poster="media/woods/stream.webp"
width="720" height="1280" aria-label="Water running over green rocks"
data-deep-link-id="moss-motion" data-caption-title="Moss and Motion"
muted loop playsinline preload="none"></video>
</figure>
</template>
</article>
</div>| Attribute or element | Meaning |
|---|---|
data-gallery-id | Required unique category ID. Used by the API. |
data-title | Text printed on the film rail. Falls back to the gallery title, then filmStripName. |
data-film-code | Smaller film rail code. Text is aligned to the right and keeps its proportions. An empty string hides it. |
data-src | Cover image or video URL. Put it on a child element inside the article. |
data-width and data-height | Actual cover dimensions. Use positive numbers with the correct aspect ratio. |
data-poster | Required poster image when data-src is a video URL. |
template[data-gallery] | Container for the category title and authored media figures. |
data-gallery-title | Required category display title, provided as text inside an element such as h2. |
figure[data-gallery-media] | One category item. Both images and videos use this wrapper. The older data-gallery-image wrapper is also accepted. |
data-gallery-src | Image or video source read by the asynchronous loader. Use this instead of src on template media. |
width and height | Required positive dimensions on every category image or video. These determine each mesh aspect ratio. |
poster | Required image on a category video. data-gallery-poster is also accepted. |
alt or aria-label | Use alt for an image and aria-label for a video to describe the media. |
data-caption-title | Optional caption title. Can be on the media or its figure. The media attribute takes precedence. |
data-caption-description | Optional caption description. Can be on the media or its figure. A figcaption text value is used as a fallback. |
data-deep-link-id | Optional stable item ID. Put it on the media or figure when you need URLs and API navigation to survive caption edits. |
Include the cover in its category template using the same source or poster URL. This lets the opening transition reuse the cover instead of adding a separate entry. When you close a category, the nearest image can become its remembered ribbon cover. The authored item indices stay the same even if display order changes.
Images can be landscape, portrait, square or panoramic. Keep their real dimensions in the markup. JPG, PNG and WebP are supported. PNG transparency is preserved over the mesh background.
Video playback and loading
Visible videos autoplay muted, loop and have no player controls. Partially visible videos can play too. The dropdown uses poster thumbnails, while category arrow hover previews can play the destination cover video.
Posters are required because they provide the loading image, stable dimensions and a fallback before a video frame is available. A poster is also used if playback is blocked or the video fails.
- Ribbon startup loads cover images and video posters. It does not download every category video before showing the ribbon.
- Category images load asynchronously, with nearby visible items prioritized. Placeholder meshes appear before the image fades in.
- Video sources are attached when needed for visible meshes. Matching sources share playback resources across the ribbon and category transitions.
- Hidden videos pause. After about 1.5 seconds offscreen, their source and texture can be released. Playback time is remembered for a later return.
- A hidden tab, gallery pause or destroy stops video playback. Browser autoplay restrictions can still require a pointer or keyboard interaction.
Use your own browser-compatible MP4 files and poster images. Add their URLs to the gallery markup and set width and height to the actual video dimensions.
Opening a link
A ribbon frame can open your URL instead of a category. Add data-link to the article or its cover element. This works with the supplied GLB film model.
For a link entry, omit data-gallery-id and template data-gallery. Keep the cover image or video, title and optional film code. If category markup is present, the category takes precedence over the link.
<article data-title="VIEW PROJECT" data-link="https://example.com/project" data-target="_blank">
<div data-src="media/project.webp" data-width="1280" data-height="853"></div>
</article>The frame centers and opens the URL without the category zoom. Use data-target="_self" for the current tab or _blank for a new tab. The default is _blank.
Set useActionOnlyOnCenterMesh: true if the first click on an off-center frame should only center it. Click the centered frame again to open its link or category. With false, a single click centers the frame and performs its action.
No action
A filmstrip frame can display a photo or video without opening a category or link. Keep its cover markup and omit data-gallery-id, template[data-gallery] and data-link from the article and its children. No extra JavaScript setting is required.
<article data-title="QUIET MOMENT" data-film-code="FRAME 01">
<div data-src="media/quiet-moment.webp" data-width="1280" data-height="853"></div>
</article>Clicking the frame centers it in the filmstrip. It does not zoom, open a category or follow a link, even when clicked again in the center. Dragging, hover effects and visible video playback remain available. This behavior also applies with the supplied GLB film model.
The useActionOnlyOnCenterMesh setting controls when category and link actions run. It does not add an action to a frame configured with no action.
Sizing and responsive layout
In afterparent mode, give the host an explicit height. A width of 100% and height of 100dvh produces a full viewport gallery. In responsive mode, the gallery sets its own host height from the sizing options.
displayType: 'responsive',
maxWidth: 1920,
maxHeight: 750,
autoScale: true,
autoScaleStartWidth: 1200Below autoScaleStartWidth, the responsive height follows 450 + (maxHeight - 450) * (width / autoScaleStartWidth)^2.5. At wider sizes it uses maxHeight. Responsive galleries always keep at least 450px of height, including when autoScale is disabled or maxHeight is set below 450.
The title, close button, category arrows and image navigation are positioned within the gallery. They scroll with the gallery on a long page. They do not follow the browser edges independently.
The ribbon and category row wrap only when enough media cover the view. A short collection uses finite navigation. Category images retain their own aspect ratios, so their widths and visible count can differ.
Most settings are read during construction. Changing the original settings object later does not rebuild the gallery. Use API methods for navigation. To apply a different configuration, call destroy() and create a new instance. Call resize() after a layout change that does not trigger a browser resize.
Settings reference
The values below are examples from the current root index.html, not a list of omitted-value defaults. Keep the complete configuration from a working demo when starting a new integration. Some parser defaults differ from the demo values, especially optional effects and developer controls.
Booleans are unquoted. Durations are in seconds. Rotation is in radians. Caption and category spacing is in CSS pixels, while ribbon dimensions and transforms are in scene units.
The two category navigation flags are currently spelled showCateogryNavigationArrows and showCateogryNavigationSelect. Use these exact names. Enable both, either one, or neither. They replace the old categorySelectorType option.
General settings
instance
Unique name for this gallery. The instance is also registered as window[instance]. Use a different name for each gallery.
parentId
ID of the visible element that holds the canvas. Create this element before constructing the gallery.
carouselDataId
ID of the hidden element containing the ribbon articles and category templates.
displayType
Use "afterparent" to follow the host width and height. Use "responsive" to let the gallery calculate its height from maxHeight, autoScale and autoScaleStartWidth.
initializeWhenVisible
Wait until the host enters the viewport before starting data and media loading.
deepLinking
Read and write the category and item in the URL hash. Defaults to false when omitted. Enable this on only one gallery per page.
backgroundColor
Canvas background color. Match the surrounding page when you want the gallery to blend into the layout.
maxWidth
Maximum canvas width in CSS pixels for responsive mode. In afterparent mode the host controls the width.
maxHeight
Maximum canvas height in CSS pixels for responsive mode. Values below 450 use the minimum height of 450 pixels. In afterparent mode set a height on the host instead.
autoScale
In responsive mode, reduce the gallery height on narrower screens toward a minimum of 450 pixels. Has no sizing effect in afterparent mode.
autoScaleStartWidth
Container width in CSS pixels below which responsive height starts scaling. Used when autoScale is true.
showPreloader
Show the flat cover sequence before the ribbon intro. Video covers use their posters in this sequence.
preloaderImageDelay
Minimum time between cover previews in seconds. Values below 0.05 are clamped to 0.05.
preloaderColor
Color of the fallback SVG loader used without a film model. This does not recolor the photo sequence used by the current ribbon.
scrollSpeedStrength
Movement sensitivity shared by ribbon and category navigation. Larger values increase the response to input.
useBlackAndWhite
Blend side images toward black and white while keeping the active image in color. Applies to the ribbon and opened category.
opacityStrength
Minimum opacity of ribbon images away from the center, from 0 to 1. Use 1 to keep side frames fully visible.
imageSharpenStrength
Image sharpening amount from 0 to 1. Values above 1 are clamped to 1, including the current index value shown here.
stats
Display the developer performance monitor. Keep false on a published gallery unless you need to inspect rendering performance.
gui
Display the live developer settings panel. Keep false for the normal visitor interface.
Ribbon settings
filmFrameModel
URL of the supplied film_frame.glb holder. Required for the current film ribbon and Three.js category experience.
frameWidth
Film frame width in scene units. The frameWidth / frameHeight ratio controls the ribbon photo proportions and related frame previews.
frameHeight
Film frame height in scene units. Category images use their own authored width and height instead of this fixed ratio.
filmStripGap
Extra spacing between neighboring frames in the main horizontal filmstrip, measured in 3D scene units rather than pixels. Use 0 to keep the filmstrip connected. Increase this value to separate its frames. This does not change the spacing between images inside an opened category or the temporary intro spread.
filmStripName
Fallback film rail title when an article has no title. An article data-title or gallery title takes precedence.
filmStripColor
Base material color of the GLB holder. Lighting affects the rendered shade. Also used by the category frame previews.
filmStripTextColor
Color of the labels drawn on the film rails.
useRibbonMouseWheel
Enable mouse-wheel and trackpad navigation over the ribbon. Dragging and touch swiping still work when false.
infinite
Allow the ribbon to wrap when there are enough frames to cover the view. The gallery falls back to finite navigation when repeating would leave a gap.
snap
Settle the ribbon toward a centered frame after movement. Clicking a frame can center it regardless of this setting.
useActionOnlyOnCenterMesh
When true, clicking an off-center ribbon frame only centers it. Click again once centered to open its category or link. When false, the first click centers the frame and performs its action.
useFilmCurve
Enable the ribbon curve. Set false for a straight ribbon. Category and selector bending have their own settings.
centerFrameFlattenStrength
Flatten the center ribbon frame, from 0 to 1. A larger value reduces its local bend.
scrollBendStrength
Extra ribbon bending caused by movement. Use 0 to remove movement-driven bending while keeping the resting curve.
scrollScaleStrength
Scale the ribbon group in response to movement speed. Use 0 to keep the group scale steady.
filmIntroDuration
Base intro fade duration in seconds. The center preview morph and side reveals share a coordinated schedule. Stagger and side delays can extend the full intro.
filmIntroStaggerDelay
Additional reveal delay in seconds for each step away from the center frame.
filmIntroSideDelay
Extra reveal delay in seconds for side frames, giving the center frame a head start.
filmIntroSpread
Extra spacing measured in frame widths. Used by the ribbon intro and the category opening, switching and closing transitions. It is not a pixel distance.
x
Horizontal position of the entire ribbon group in scene units.
y
Vertical position of the entire ribbon group in scene units.
z
Depth position of the entire ribbon group in scene units.
rotationX
Rotation of the entire ribbon group around X, in radians.
rotationY
Rotation of the entire ribbon group around Y, in radians.
rotationZ
Rotation of the entire ribbon group around Z, in radians.
cameraPositionX
Horizontal camera offset in scene units.
cameraPositionY
Vertical camera offset in scene units.
Category settings
useCategoryMouseWheel
Enable mouse-wheel and trackpad navigation over the opened category. Independent of ribbon and dropdown wheel settings.
categoryImageGap
Horizontal spacing between images inside an opened category, measured in CSS pixels. Galleries narrower than 600 pixels use half this value. Use filmStripGap to change spacing between the main filmstrip frames instead.
categoryBendStrength
Shared bending of category images while moving, clamped from 0 to 1. Use 0 for a flat category row.
categoryImageBackgroundColor
Placeholder color visible before a category image or video poster finishes loading and fading in.
categorySwitchDuration
Outgoing fade and incoming reveal timing in seconds when changing categories. Clamped from 0.1 to 2.
categoryButtonColor
Base color of close and navigation button discs and hover outlines. Difference blending applies only where the controls overlap category photos.
categoryButtonIconColor
Color of the symbols inside the category buttons.
categoryTextColor
Base color of the opened category title, before image-only difference blending.
categoryImageCounterColor
Color of the total image count and separator between the image navigation arrows.
categoryCurrentImageNumberColor
Color of the current image number between the image navigation arrows.
Caption settings
categoryCaptionEnabled
Show captions authored on category images and videos. Items without caption text remain caption-free.
categoryCaptionSmallViewportWidth
Gallery viewport width in CSS pixels at which the small caption values apply. Caption sizing follows the gallery viewport, not the image aspect ratio.
categoryCaptionFullSizeViewportWidth
Gallery viewport width in CSS pixels at which the full caption values apply. Values interpolate between the two width thresholds.
categoryCaptionTitleFontFamily
CSS font-family used for caption titles. Load the font before constructing the gallery.
categoryCaptionDescriptionFontFamily
CSS font-family used for caption descriptions. Load the font before constructing the gallery.
categoryCaptionTitleFontSize
Full-size caption title font size in CSS pixels, clamped from 1 to 200.
categoryCaptionTitleFontSizeSmall
Caption title font size at the small viewport threshold, in CSS pixels.
categoryCaptionDescriptionFontSize
Full-size caption description font size in CSS pixels, clamped from 1 to 200.
categoryCaptionDescriptionFontSizeSmall
Caption description font size at the small viewport threshold, in CSS pixels.
categoryCaptionTitleFontWeight
Caption title font weight from 1 to 1000. The strings "normal" and "bold" also map to 400 and 700.
categoryCaptionDescriptionFontWeight
Caption description font weight from 1 to 1000. The strings "normal" and "bold" also map to 400 and 700.
categoryCaptionTitleFontStyle
Caption title font style. Accepts "normal", "italic" or "oblique".
categoryCaptionDescriptionFontStyle
Caption description font style. Accepts "normal", "italic" or "oblique".
categoryCaptionTitleLineHeight
Unitless title line-height multiplier from 1 to 4. Applies within a wrapped title, not to the title-to-description gap.
categoryCaptionDescriptionLineHeight
Unitless description line-height multiplier from 1 to 4.
categoryCaptionTitleLetterSpacing
Additional spacing between title characters in CSS pixels, from -10 to 30.
categoryCaptionDescriptionLetterSpacing
Additional spacing between description characters in CSS pixels, from -10 to 30.
categoryCaptionTitleMaxLines
Maximum title lines from 0 to 20. Use 0 to hide the title block. The caption height budget can reduce the visible text further.
categoryCaptionDescriptionMaxLines
Maximum description lines from 0 to 20. Use 0 to hide the description block. The caption height budget can reduce the visible text further.
categoryCaptionTitleColor
Caption title text color.
categoryCaptionDescriptionColor
Caption description text color.
categoryCaptionAlign
Caption text alignment. Accepts "left", "center" or "right".
categoryCaptionPaddingX
Full-size horizontal inset of the caption from the image edge, in CSS pixels.
categoryCaptionPaddingXSmall
Horizontal caption inset at the small viewport threshold, in CSS pixels.
categoryCaptionPaddingY
Full-size inset between the caption and the bottom image edge, in CSS pixels. This is not a browser-edge margin.
categoryCaptionPaddingYSmall
Bottom caption inset at the small viewport threshold, in CSS pixels.
categoryCaptionGap
Full-size visible gap between the title and description, in CSS pixels. Measured from the drawn text bounds so descenders do not add extra spacing.
categoryCaptionGapSmall
Title-to-description gap at the small viewport threshold, in CSS pixels.
categoryCaptionMaxHeight
Maximum fraction of image height available to the caption, from 0 to 1. Text may be shortened to fit this budget.
categoryCaptionGradientColor
Color behind the lower caption area.
categoryCaptionGradientOpacity
Maximum caption gradient opacity from 0 to 1.
categoryCaptionGradientHeight
Extra gradient coverage above the full-size caption area, in CSS pixels.
categoryCaptionGradientHeightSmall
Extra gradient coverage at the small viewport threshold, in CSS pixels.
categoryCaptionFadeInStart
Opening progress at which captions start fading in, from 0 to 0.99. This is a progress fraction, not seconds.
categoryCaptionFadeOutDuration
Caption fade-out duration in seconds.
Category navigation settings
showCateogryNavigationArrows
Show previous and next category arrows beside the close button. Keep this exact property spelling, including "Cateogry".
showCateogryNavigationSelect
Show the category dropdown trigger. Independent of the category arrows. Keep this exact property spelling, including "Cateogry".
categorySelectorTextColor
Color of the category navigation label and total category count.
categorySelectorCurrentNumberColor
Color of the bold current category number beside the category arrows.
categorySelectorPreviewWidth
Preferred hover preview width in CSS pixels, clamped from 160 to 480. The preview can shrink to fit the gallery.
categorySelectorPreviewFontFamily
CSS font-family used in the category arrow hover preview.
categorySelectorPreviewLabelFontSize
Font size of the previous/next category label and count in the hover preview, in CSS pixels from 8 to 24.
categorySelectorPreviewTitleFontSize
Font size of the destination category title in the hover preview, in CSS pixels from 10 to 32.
categorySelectorPreviewLabelFontWeight
Hover preview label weight from 100 to 900.
categorySelectorPreviewTitleFontWeight
Hover preview title weight from 100 to 900.
categorySelectorPreviewTextGap
Gap between the hover preview metadata and category title, in CSS pixels from 0 to 80.
categorySelectorPreviewPaddingTop
Top padding of the text area below the preview frame, in CSS pixels from 0 to 80.
categorySelectorPreviewPaddingX
Horizontal padding of the preview text area, in CSS pixels from 0 to 80.
categorySelectorPreviewPaddingBottom
Bottom padding of the preview text area, in CSS pixels from 0 to 80.
categorySelectorPreviewLabelColor
Color of the previous/next category label in the hover preview.
categorySelectorPreviewTitleColor
Color of the destination category title in the hover preview.
categorySelectorPreviewCounterColor
Color of the total category count and separator in the hover preview.
categorySelectorPreviewCurrentNumberColor
Color of the bold destination category number in the hover preview.
categorySelectorLabels
Text and accessible labels for category arrows and hover previews. See the label table below for every key and placeholder.
Category dropdown settings
useCategoryPickerMouseWheel
Enable mouse-wheel and trackpad scrolling inside the vertical category dropdown. Does not change wheel input on the main ribbon or category images.
categoryPickerLabels
Text and accessible labels for the category dropdown. See the label table below for every key and placeholder.
categoryPickerWidth
Preferred dropdown width in CSS pixels from 220 to 480. It shrinks to fit narrow galleries.
categoryPickerHeight
Preferred dropdown height in CSS pixels from 260 to 900. Available gallery space can reduce the actual height.
categoryPickerFrameHeight
Category thumbnail frame height in CSS pixels from 100 to 400. Frame proportions follow frameWidth and frameHeight.
categoryPickerBendStrength
Bend strength of the vertical category ribbon, from 0 to 1. Use 0 for straight thumbnails.
categoryPickerBackgroundColor
Background color of the category dropdown.
categoryPickerTextColor
Main dropdown text color and fallback for several more specific colors.
categoryPickerFontFamily
CSS font-family used throughout the category dropdown.
categoryPickerHeaderColor
Background color of the dropdown header area.
categoryPickerSecondaryTextColor
Color of secondary dropdown text, including media count wording and selection status.
categoryPickerPhotoCountColor
Color of the bold media count number in the dropdown.
categoryPickerCounterColor
Color of the total category count and separator in the dropdown.
categoryPickerCurrentNumberColor
Color of the bold selected category number in the dropdown.
categoryPickerTitleColor
Color of the category title in the dropdown.
categoryPickerBorderColor
Color used for the dropdown border.
categoryPickerShadowColor
CSS color, including rgba(), used by the dropdown shadow.
categoryPickerFocusColor
Keyboard focus indicator color in the dropdown.
categoryPickerSelectionColor
Color of the marker beside the selected category. The marker stays on the selected category while other thumbnails are hovered.
categoryPickerScrollbarTrackColor
Custom scrollbar track color.
categoryPickerScrollbarThumbColor
Custom scrollbar handle color.
categoryPickerCloseButtonColor
Dropdown close button disc and outline color.
categoryPickerCloseButtonIconColor
Dropdown close button icon color.
Post-processing settings
antialias
Smooth geometry edges. The renderer uses multisampling where supported and an FXAA fallback otherwise.
waveFrequency
Spatial frequency of the wave distortion. Higher values place more waves across the image. This does not control animation speed.
waveAmplitude
Wave distortion strength. Use 0 to disable it. The Waves demo uses 0.06 with a frequency of 0.12.
useFlowmap
Enable pointer-driven flowmap distortion on the ribbon and opened category.
flowmapStrength
Flowmap displacement strength from 0 to 5.
useNoise
Enable noise, blur and edge fading on the shared ribbon/category rendering.
noiseSize
Controls noise coverage. Smaller values leave a larger clear center. Use 0.12 as a restrained starting point.
noiseFadeSize
Width of the edge fade relative to the canvas width. Smaller values narrow the faded region.
noiseBlurStrength
Blur strength in the noise region. The Noise demo uses 0.12.
noiseFadeColor
Color of the outer fade. Use "#ffffff" on a white page.
grid
Enable pointer-driven grid displacement. This is an image distortion effect, not a tiled gallery layout.
gridAddRGBDistortion
Add color-channel separation to the grid effect. The current Grid demo keeps this false.
gridSize
Number of grid columns. Rows follow the canvas proportions. Use a positive integer. The parser caps the value at 400, so the current index value of 1600 is treated as 400. The Grid demo uses 24.
gridMouseRadiusFactor
Radius of the affected grid area relative to the grid size. Larger values affect more cells. Values above 2 are capped at 2.
gridMouseStrengthFactor
Pointer displacement strength. Values above 2 are capped at 2. The Grid demo uses 0.9.
gridMouseRelaxation
Amount of displacement retained each update. Use a value from 0 to 1. Values nearer 1 take longer to settle.
Text labels and placeholders
Pass only the label keys you want to change. Missing keys keep their built-in text. An empty string is allowed. Labels are treated as text, not HTML. Keep any placeholders that your translated text needs.
categorySelectorLabels
| Key | Example text | Meaning |
|---|---|---|
heading | Categories | Category navigation heading. |
counter | {current} / {total} | Category number. Supports {current} and {total}. |
navigation | Category navigation | Accessible name of the category navigation group. |
previous | Previous category: {title} | Previous category button label. Supports {title}. |
next | Next category: {title} | Next category button label. Supports {title}. |
previewPrevious | Previous category | Previous category preview label. |
previewNext | Next category | Next category preview label. |
categoryPickerLabels
| Key | Example text | Meaning |
|---|---|---|
heading | COLLECTIONS | Dropdown heading. |
mediaCountOne | {count} item | Singular mixed-media count. Supports {count}. |
mediaCountMany | {count} items | Plural mixed-media count. Supports {count}. |
photoCountOne | {count} photo | Singular photo-only count. Supports {count}. |
photoCountMany | {count} photos | Plural photo-only count. Supports {count}. |
current | Currently viewing | Status for the currently selected category. |
open | Open collection | Status for a different category. |
details | {photos} ยท {status} | Combined footer text. Supports {photos} and {status}. |
counter | {current} / {total} | Category position. Supports {current} and {total}. |
dialog | Choose a collection | Accessible name of the dropdown dialog. |
close | Close collection picker | Accessible name of its close button. |
scroll | Scroll collections | Accessible name of the custom scrollbar. |
option | {title}, {photos} | Accessible thumbnail label. Supports {title} and {photos}. |
chooseCategory | {title}: choose collection | Category title trigger label. Supports {title}. |
openCategory | Open {title} | Thumbnail action label. Supports {title}. |
returnToCategory | Return to {title} | Selected thumbnail action label. Supports {title}. |
categorySelectorLabels: {
heading: 'Collections',
previous: 'Previous collection: {title}',
next: 'Next collection: {title}'
},
categoryPickerLabels: {
heading: 'CHOOSE A COLLECTION',
counter: '{current} / {total}',
close: 'Close collections'
}Caption layout
Caption title and description are independent. Leave both attributes out for no caption, or provide only the title. Keep descriptions short enough to read while browsing. Long text wraps and can be shortened by the line and height limits.
Typography and spacing use the gallery viewport width rather than the image width. A square image and a wide image therefore use the same font size and title-to-description gap at the same gallery size. Small-screen values interpolate between the two caption viewport thresholds.
- Use categoryCaptionGap and categoryCaptionGapSmall for the gap between title and description.
- Use categoryCaptionPaddingY and categoryCaptionPaddingYSmall for the distance from the caption to the bottom of the image.
- Use the line-height settings for spacing within wrapped text. They do not replace the title-to-description gap.
- Load custom fonts before initialization. Captions are drawn into textures and do not inherit ordinary page paragraph styles.
categoryCaptionTitleFontFamily: 'Roboto, sans-serif',
categoryCaptionDescriptionFontFamily: 'Roboto, sans-serif',
categoryCaptionTitleFontSize: 23,
categoryCaptionTitleFontSizeSmall: 16,
categoryCaptionDescriptionFontSize: 15,
categoryCaptionDescriptionFontSizeSmall: 14,
categoryCaptionGap: 6,
categoryCaptionGapSmall: 2,
categoryCaptionPaddingY: 26,
categoryCaptionPaddingYSmall: 14Deep linking
Set deepLinking: true to open categories and media from the URL and update the hash as the selection settles. It is false by default. Browser Back and Forward restore routes.
#category=Salt_Sea&item=An_Open_BlueCategory routes are based on their titles. Item routes use data-deep-link-id first, then the caption title, then a fallback item name. Spaces and most punctuation become underscores. Duplicate names receive suffixes such as ~2. Non-ASCII characters may still be URL-encoded by the browser.
Use stable, unique item IDs for links you plan to share. A category ID is also accepted when reading an incoming category route. An unknown category is ignored, while a missing or unknown item opens the first item of a known category.
The route waits for loading and compatible transitions. Pointer, wheel or keyboard input can interrupt a pending route. Outgoing changes are published after movement settles. Closing returns to the ribbon route. Query parameters and unrelated hash values are preserved where applicable.
Enable deep linking on only one instance per page because all instances share the same location hash. If your site already uses a hash router, coordinate ownership of the category and item parameters.
JavaScript API
Keep the object returned by new FWDFPAVS(settings). Register loading callbacks in the events constructor setting and wait for ready before calling navigation methods. Most navigation calls return immediately with a boolean, they do not return an animation promise.
A true result means the request was accepted, not that an animation or media download has finished. A false result means the request is unavailable, invalid or blocked by the current transition. Listen for the matching event when completion matters.
getState()
Return a snapshot of readiness, playback, view, transition, selection, selector visibility and canvas dimensions. It does not expose live Three.js objects.
getCategories()
Return category metadata and authored media lists. Available after dataReady. Before data exists the result is an empty array.
openCategory(id)
Open or switch to a category using a zero-based ribbon index, an exact data-gallery-id, or an exact gallery title. Wait for ready before the first call. Returns false for an unknown category, the already-open category, or an incompatible transition.
openGallery(id)
Compatibility alias for openCategory(id). Use openCategory in new integrations.
closeCategory()
Request the reverse transition back to the ribbon. Returns false if there is no category or it is already closing. Use categoryClosed to know when the transition has finished.
nextCategory()
Switch to the next authored category. Requires an open category and wraps after the last category.
previousCategory()
Switch to the previous authored category. Requires an open category and wraps before the first category.
nextItem()
Request the next media item in a ready category. Uses the category easing and wrap rules. At a finite endpoint the request may be accepted without moving beyond the endpoint.
previousItem()
Request the previous media item in a ready category. Uses the same easing and finite endpoint behavior as nextItem().
goToCategoryItem(id)
Center an item by its authored zero-based index, explicit data-deep-link-id, generated deep-link key, or exact caption title. Requires a ready category. Duplicate titles resolve to the first match, so use a unique ID or index.
goToItem(index)
Center a ribbon frame by zero-based index. This method is for the minimized ribbon and returns false while a category is open or the intro prevents navigation.
pause()
Pause gallery rendering and visible video playback. This is not an autoplay-slideshow control. Manual pause remains active across scrolling and tab visibility changes.
play()
Clear a manual pause and resume rendering and visible video playback when the document is visible.
resize()
Measure the host and refresh the canvas layout. Useful after changing a surrounding container without a window resize. Call after the host has its new dimensions.
destroy()
Stop loading and animation work, pause videos, remove owned DOM and listeners, dispose GPU resources, and unregister the instance. Safe during loading, opening, switching or closing. Calling it again is safe. The host and source markup remain available for a new instance.
on(type, callback)
Subscribe to an event. Register the same type and callback once. Use the events constructor setting to capture events from the start of loading.
off(type, callback)
Remove a listener using the same callback reference. Also removes a once listener by its original callback.
once(type, callback)
Subscribe to one occurrence. The listener is removed before its callback runs.
addEventListener(type, callback)
Equivalent to on(type, callback).
removeEventListener(type, callback)
Equivalent to off(type, callback).
function openThirdCoastImage() {
const onOpened = event => {
if (event.category.id !== 'coast') return
gallery.off('categoryOpened', onOpened)
gallery.goToCategoryItem(2)
}
gallery.on('categoryOpened', onOpened)
if (!gallery.openCategory('coast')) {
gallery.off('categoryOpened', onOpened)
}
}State and metadata
getState() and getCategories() return plain snapshots. Read them again when you need current values. Category and item indices start at zero. Authored indices stay stable when a remembered cover changes display order.
| getState() field | Meaning |
|---|---|
ready | Whether the initial gallery readiness requirements have finished. |
destroyed | Whether destroy() has run. |
playing | Whether the render loop is running. |
view | "ribbon" or "category". |
transition | "intro", "opening", "closing" or null. |
ribbonIndex | Current zero-based ribbon selection, or null before selection is known. |
category | Current category metadata, or null. |
item | Nearest category item metadata, or null. |
selectorOpen | Whether the category dropdown is open. |
width, height | Canvas dimensions in CSS pixels. |
A category snapshot contains index, id, title and itemCount. getCategories() adds an items array for each category.
An item snapshot contains index, displayIndex, id, title, description, src, poster, mediaType, width, height and loaded. mediaType is "image" or "video". displayIndex is null when no current display position was supplied. loaded describes image or poster availability, not video buffering or permanent GPU residency.
Events
Every callback receives type and target, plus the fields below. target is the gallery instance. Use an event string such as "categoryOpened" or its constant FWDFPAVS.EVENTS.CATEGORY_OPENED.
const gallery = new FWDFPAVS({
...settings,
events: {
ready(event) {
console.log(event.target.getState())
},
categoryOpened(event) {
console.log(event.category.title, event.item.title)
},
categoryClosing(event) {
if (event.reason === 'close') showPageMenu()
},
error(event) {
console.error(event.message)
}
}
})
function onItemChange(event) {
console.log(event.item.id)
}
gallery.on('categoryItemChange', onItemChange)
gallery.off('categoryItemChange', onItemChange)dataReady
Ribbon cover data is available. getCategories() can now be read. This does not mean the model and intro are finished.
Additional fields: categories.
loadProgress
A ribbon cover photo or poster finishes loading. progress is a fraction from 0 to 1. This is not a count of all category assets.
Additional fields: loaded, total, progress, index, src.
ready
The ribbon has completed its loading and intro requirements and can accept API navigation. Fires once per instance.
Additional fields: categories.
error
A gallery data, cover or model error is reported. Category image failures and video failures have separate events.
Additional fields: message, text.
introStart
The ribbon intro starts.
Additional fields: No additional fields.
introComplete
The ribbon intro finishes. Intro events may be absent when no intro runs.
Additional fields: No additional fields.
itemUpdate
The centered ribbon frame changes. id is its zero-based ribbon index.
Additional fields: id, category.
categoryChange
The active category changes, including return to the ribbon. category and item are null on return.
Additional fields: category, item, previousCategory.
categoryOpening
A category starts appearing. reason is "open" or "switch".
Additional fields: category, item, reason.
categoryOpened
The category opening or switching entrance finishes.
Additional fields: category, item.
categoryClosing
A category starts leaving. reason is "close" or "switch".
Additional fields: category, item, reason.
categoryClosed
The outgoing category is disposed after its exit. reason is "close" or "switch". Destroying the whole gallery emits destroy instead.
Additional fields: category, item, reason.
categoryItemChange
The nearest category item changes, including items passing the center while dragging. It does not wait for movement to settle.
Additional fields: category, item, previousItem.
mediaLoad
A category image or poster becomes available. kind is "image" or "poster". Cached media can emit this when a category is entered again.
Additional fields: category, item, kind.
mediaError
A category image or poster fails to load.
Additional fields: category, item, kind.
videoPlay
A pooled visible video starts playing. src is the absolute video URL. Shared video sources emit one playback event per source, not one per mesh.
Additional fields: src.
videoPause
A pooled video pauses.
Additional fields: src.
videoError
A video source reports a browser media error. Its poster remains the fallback.
Additional fields: src, code, message.
dragStart
Dragging begins. scope is "ribbon", "category" or "categorySelector".
Additional fields: scope, category, item.
dragEnd
Dragging ends. Inertia may continue after this event.
Additional fields: scope, category, item.
categorySelectorOpen
The category dropdown opens.
Additional fields: category, item.
categorySelectorClose
The category dropdown closes.
Additional fields: category, item.
deepLinkChange
A route is read or published. direction is "incoming" or "outgoing". Incoming routes can be announced before their visual transition.
Additional fields: url, hash, direction.
resize
The canvas dimensions change. Values are in CSS pixels.
Additional fields: width, height.
play
Rendering starts or resumes, including automatic visibility changes.
Additional fields: No additional fields.
pause
Rendering stops, including automatic visibility changes.
Additional fields: No additional fields.
destroy
Cleanup has finished. Dispatched once, synchronously after cleanup. No later events come from that instance.
Additional fields: No additional fields.
Callbacks normally run after the current internal update. Navigation or destroy() can be called from a callback. The destroy event is the exception and runs synchronously after cleanup.
Category switching overlaps an outgoing fade with an incoming reveal. Do not assume categoryClosed always occurs before categoryOpening. Check the category and reason fields. Pausing the render loop also pauses events that depend on animation progress.
Cleanup and multiple galleries
Call destroy() before removing or replacing a gallery in a component, tab or client-side route. You can call it while assets are loading or an animation is running. You do not need to wait for ready.
gallery.destroy()
// The same host and instance name can now be reused.
const replacement = new FWDFPAVS({
...settings,
instance: 'exampleGallery',
parentId: 'gallery-holder',
carouselDataId: 'gallery-data'
})Destroy restores the host inline position, width and height and removes the gallery-owned canvas and controls. It leaves your host and hidden HTML data in place. Remove your own application listeners and DOM separately if your component created them.
For multiple galleries, provide unique instance names and host IDs. Each instance has its own API and events. Keep deep linking enabled on at most one of them. Avoid initializing many visible video galleries at once.
Media performance
- Resize photos close to their displayed size. The included demo uses many images around 1280 pixels on the long side. Larger screens may justify larger files, but excessive dimensions increase texture memory.
- Use actual dimensions in the markup. Incorrect values change the mesh proportions and can cause visible layout shifts.
- Prepare video posters with the same aspect ratio as their videos. Give each different video a distinct poster URL so cover matching stays unambiguous.
- Compress video for the intended display size. Full-length videos are supported, but low bitrate and suitable resolution still matter.
- Keep the effect count small on mobile. Grid cost grows with both its rows and columns. A 24-column grid is much lighter than a 400-column grid.
- Use stats and gui only while developing. Reduce sharpening if fine image detail looks harsh.
- Keep the supplied model and complete asset paths together when publishing beneath a subdirectory.
- Allow cross-origin media requests when serving textures from another host. Video delivery should support byte-range requests for buffering and seeking.
The slider already limits work for offscreen meshes, loads category images asynchronously, shares visible video resources and releases inactive playback resources. These measures do not remove the cost of very large source files.
Troubleshooting
| Problem | What to check |
|---|---|
| Blank gallery | Check that parentId and carouselDataId exist before initialization. In afterparent mode the host must have a nonzero height. Inspect the browser console for a data or WebGL error. |
| Model or decoder 404 | Publish the complete output and verify the filmFrameModel URL. The current standalone demo bundle embeds its decoder. An old bundle requesting a missing /assets/draco_decoder file should be rebuilt. |
| Vite dependency 504 during development | Stop the development server, clear its node_modules/.vite cache, restart it and reload the page. This is a development dependency-cache issue, not a production media URL. |
| Images fail online | Check the exact filename and letter case, HTTP status, base path and cross-origin permission. Local Windows paths such as D:\work are not public media URLs. |
| Video stays on its poster | Check the video request, browser codec support and autoplay policy. Listen for videoError. A blocked autoplay attempt can be retried after user input. |
| A category opens with unexpected proportions | Verify width and height on each category media element. Ribbon frameWidth and frameHeight do not replace category dimensions. |
| Captions look wrong after a font change | Load the font first, then recreate the gallery. Confirm that the requested font weight and style are available. |
| Mouse wheel moves the page | Enable the flag for the specific area you want to control. Ribbon, category images and dropdown have separate wheel settings. |
| A short gallery does not loop | Wrapping is disabled when there are not enough frames to cover the view safely. Add more items or keep finite navigation. |
| An API request returns false | Check getState(), the item/category identity and the current transition. Wait for ready or categoryOpened where appropriate. |
| A setting change has no effect | Check the exact name, native boolean type and documented units. Recreate the instance after changing constructor settings. |
| A shared link opens the wrong item after editing captions | Use unique data-deep-link-id values and keep them stable. Titles are used as route fallbacks. |
For support, include the page URL, browser, console error, relevant settings and a small markup example. Contact tibi@fwdapps.net.
Working with the source project
The source project uses ES modules, Three.js and Vite. Install dependencies from the project folder, then start the local server or create a build.
npm install
npm run dev
npm run build
npm run build:demo
npm run build:docsnpm run build refreshes the standalone constructor and creates build/start.html with its complete asset folders, alongside the built development demo. npm run build:demo updates the standalone demo script, media and preset pages. Use the complete output for the workflow you choose. The standalone library used in this guide is demo/js/filmstrip-gallery.js.
Edit the authored media and main example settings in index.html. Demo-specific overrides live in tools/build-demo-pages.mjs. This JavaScript guide is generated by tools/build-javascript-docs.mjs, which checks that every current index setting and public event has a description.
Legacy carousel effects removed from the exposed settings are not part of the current guide. Use the current frame, category and effect options above rather than older examples with string booleans, planeWidth/planeHeight or categorySelectorType.