Spins
The Spins package in Flowplayer is a lightweight, TypeScript-based package designed to create short-form video experiences using Flowplayer OVP playlists or inline arrays of video items. It provides a vertical video player component, optimized for formats like TikTok©, Instagram© Reels, and YouTube© Shorts. This package automatically handles playlist loading and provides the structure needed for smooth, mobile-optimized video playback.
Spins is currently in beta. Features, APIs, and behaviors may change as development continues. We encourage testing in staging environments and welcome your feedback.
Table of contents
Before you start
Before you start working with the Spins package, we make the following assumptions and recommendations:
-
You're familiar with the ECMAScript Module (ESM) approach of handling JavaScript modules.
-
You have Node.js and Node Package Manager (npm) installed on your machine.
-
Review the Token configuration section. To authorize the player, you need to configure it with your token.
-
Creating tokens and setting up playlists requires a Flowplayer OVP subscription .
Install Spins
The @flowplayer/spins package is available for download via npm (recommended), yarn, or as an ES (ECMAScript) module. It automatically includes all minimum player components and CSS to work with Flowplayer.
// Install the spins package from npm
npm install @flowplayer/spins
// Import the package for use
import { createSpins, spinEvents } from "@flowplayer/spins";
yarn add @flowplayer/spins
<script type="module">
import {createSpins, spinEvents} from "https://player.wowza.com/spins/stable/index.js"
</script>
Configure Spins
To use the Spins package, configure it with the createSpins() method. There are two ways to provide video content to Spins:
Using a Flowplayer OVP playlist ID – Use the playlist ID of an existing playlist from your account, and Spins will automatically load and display the content.
Using an array of SpinItem objects – Use this method to manually define each video clip, including source URL, title, poster image, description, and plugins.
-
- Property
- Description
- Type
-
playlist- Defines an OVP playlist ID
- string (required if no ``´spinsArray``` is configured)
-
spinItem[]- Defines an array of SpinItem objects
- array (required if no
playlistobject is configred)
-
lang- Sets the language of the player UI and messages. For example,
enfor English. - string (optional)
-
token- The player token
- string (required)
-
ui- Sets UI configuration flags for customizing player appearance and behavior. Not all options will be available.
- bitmask (optional)
-
ima- Configures the Ads plugin for use with the spins container. For configuration details, see the Ads plugin.
- array (optional)
-
adFrequency- Displays ads at the specified spin interval. For example, a value of 3 displays ads on the 3rd, 6th, 9th, and subsequent spins. The default frequency is 10, meaning ads display every 10th spin. This property requires you to set the ima property. If
imais not set, ads are not displayed. Ifimais set butadsFrequencyis not, ads default to every 10th spin. Ignored ifadPositionsis set. - integer (optional; default: 10)
-
firstAdIndex- Index of the first ad spin; sets the starting point
adsFrequencycounts from. Ignored if adPositions is set. - integer (optional, default: index equals
adFrequnecy)
-
adPositions- Explicit list of spin indexes to show ads on (e.g. [2, 7, 11]); overrides
adsFrequency/firstAdIndex - array of integers (optional)
-
articleLabel- Label for article links
- string (optional, default: "Read more")
-
closeButton- Render a close button overlay
- boolean (optional, default:
false)
-
share- Configures the Share plugin for use with the Spins container.
- object (optional)
-
consent- Configures the Consent plugin for use with the spins container.
- bitmask (optional)
-
plugins- Sets plugins to include with the player. For example:
["subtitles", "vtsel", "ads", "share", "thumbnails", "asel"]. - array (optional)
SpinItem properties
Each spinItem represents a single short-form video and includes properties such as the video URL, title, poster image, and subtitles.
-
- Property
- Description
- Type
-
- url
- Defines the video source URL or media ID from OVP for the video, can be just the url/ID string or an object
{ src: string; type: string }. - string or object (required)
-
- title
- Defines a title for the video.
- string (optional)
-
- poster
- Sets a poster image shown before the video plays.
- string (optional)
-
- description
- Defines a short description of the video content.
- string (optional)
-
- articleUrl
- absolute http/https URL to the "Read More" article, rendered as a link pill.
- string (optional)
-
- subtitles
- Adds subtitles to the video.
- object (optional)
Example
The following examples use the properties from the previous table to import the Spins package from npm and to configure it with a OVP playlist ID or with an array of customized spin items. You can also see how event listeners are used to manage the spins container.
import { createSpins, spinEvents } from "@flowplayer/spins";
// Use a playlist ID from Wowza Video
const playlistID = "c30850bc-eeeb-421d-9ed3-158339d0851a";
// Create Spins using playlist ID
const container = createSpins({
token: "[your-player-token]",
playlist: playlistID,
plugins: ["subtitles"]
});
// Listen for the SPIN_IN_VIEWPORT event
container.on(spinEvents.SPIN_IN_VIEWPORT, (ev) => {
const { config, spin, index } = ev.detail;
console.log("spin_in_viewport_data: ", config, spin, index);
// Add your code here
});
// Append the container to the DOM
document.body.append(container);
import { createSpins, spinEvents } from "@flowplayer/spins";
// Manually define an array of spin items
const spinItemArray = [
{
// Add media ID for a Wowza Video asset
url: "38a31588-07a0-49bf-ba55-fa0310e13f15"
},
{
url: "https://cf23f0bd0.lwcdn.com/hls/9eda7730-23e9-4462-9ad4-c53e8004c3e9/playlist.m3u8",
title: "A title",
description: "A description",
poster: "//cf23f0bd0.lwcdn.com/i/v-i-9eda7730-23e9-4462-9ad4-c53e8004c3e9-1.jpg"
},
{
url: "https://stdlwcdn.lwcdn.com/hls/2cd21de6-1586-428b-bf7f-acc3fdcfd697/playlist.m3u8",
poster: "//stdlwcdn.lwcdn.com/i/v-i-67059b8d-ce1a-457e-9af1-d927ff945826-1.jpg",
title: "Test - missing mp4 01/09/2025 14:57"
},
{
url: "https://cdn.flowplayer.com/d9cd469f-14fc-4b7b-a7f6-ccbfa755dcb8/hls/383f752a-cbd1-4691-a73f-a4e583391b3d/playlist.m3u8?t=1",
subtitles: {
tracks: [
{
src: "https://builds.flowplayer.com/samples/en.vtt",
lang: "en",
label: "English"
},
{
src: "https://builds.flowplayer.com/samples/pt.vtt",
lang: "pt",
label: "Portuguese"
}
]
}
}
];
// Create Spins using spin item array
const container = createSpins({
playlist: spinItemArray,
plugins: ["subtitles"]
});
// Listen for the SPIN_IN_VIEWPORT event
container.on(spinEvents.SPIN_IN_VIEWPORT, (ev) => {
const { config, spin, index } = ev.detail;
console.log("spin_in_viewport_data: ", config, spin, index);
// Add your code here
});
// Append the container to the DOM
document.body.append(container);
Listen to events
This section describes the events you can capture to control and manage your spins container.
Spin events
-
- Event
- Description
-
SPIN_CREATED- Emitted when a new spin is created
-
SPIN_IN_VIEWPORT- Emitted when a spin becomes visible in the view
Media events
-
- Event
- Description
-
CAN_PLAY- Emitted when the browser can start playing the media without stopping for buffering.
-
TIME_UPDATE- Emitted when the playback position changes (e.g., during playback or scrubbing).
-
PLAYING- Emitted when playback is actively progressing after being paused or buffering.
-
WAITING- Emitted when playback is delayed while the next frame is being loaded (buffering).
-
PAUSE- Emitted when playback has been paused by the user or programmatically.
-
PLAY- Emitted when playback has been started or resumed.
-
SEEKING- Emitted when the user begins seeking to a new playback position.
-
SEEKED- Emitted when a seek operation completed.
-
ENDED- Emitted when playback has reached the end of the media resource.
Ad events
-
- Event
- Description
-
AD_TEARDOWN- Emitted when the ad teardown/cleanup process occurs.
-
AD_COMPLETED- Emitted when a single ad has finished playing.
-
AD_REQUEST_ERROR- Emitted when an ad request fails due to a network or server error.
-
AD_STARTED- Emitted when an ad starts playing.
-
AD_ERROR- Internal or general ad error event.
-
AD_PLAYBACK_ERROR- Emitted when an ad request succeeds but the ad fails to play.
-
AD_BREAK_COMPLETED- Emitted when an ad break (group of ads) has completed.
-
AD_PROGRESS- Emitted periodically as the ad progresses during playback.
-
AD_PAUSED- Emitted when an ad is paused.
-
AD_RESUMED- Emitted when an ad resumes after being paused.
-
AD_PREROLL_FINISHED- Emitted when pre-roll or post-roll playback is finished.
-
AD_SKIPPED- Emitted when the skip button is clicked and the ad is skipped.
-
AD_MUTED- Emitted when the ad has been muted.
-
AD_VOLUME_CHANGED- Emitted when the ad volume changes during playback.
SpinsContainer methods
-
- Method
- Description
-
on(event: string, handler: (ev: CustomEvent) => void)- Subscribes to an event (e.g.,
SPIN_CREATED, ``´SPIN_IN_VIEWPORT```).
-
off(event: string, handler: (ev: CustomEvent) => void)- Removes a previously added event listener.
-
addSpins(spinItems: SpinItem[])- Adds new spins to the container dynamically. Accepts an array of SpinItem objects.
-
onPlayerEvent(event: string, handler: (ev: CustomEvent) => void)- Adds a listener for any player event, including mediaEvents and adEvents.
-
offPlayerEvent(event: string, handler: (ev: CustomEvent) => void)- Removes a listener for any player event added with onPlayerEvent().
Example
This code initializes a Spins player instance using the createSpins function with a specified playlist ID, access token, and language. It also sets up a few event listeners:
import {
createSpins,
spinEvents,
mediaEvents,
adEvents
} from "@flowplayer/spins";
const container = createSpins({
playlist: "[your-playlist-id]",
token: "[your-player-token]",
lang: "en",
adsFrequency: 5,
ima: "your ads configuration"
});
// Spin lifecycle events
container.on(spinEvents.SPIN_CREATED, (ev) => {
const { config, spin, index } = ev.detail;
console.log("New spin created:", spin);
});
container.on(spinEvents.SPIN_IN_VIEWPORT, (ev) => {
const { config, spin, index } = ev.detail;
console.log("Spin is now in viewport:", spin);
});
// Media events
container.onPlayerEvent(mediaEvents.TIME_UPDATE, (ev) => {
const { spinIndex, duration, currentTime } = ev.detail;
console.log(`Progress: ${currentTime}/${duration}`);
});
// Ad events
container.onPlayerEvent(adEvents.AD_STARTED, (ev) => {
console.log(`Ad started at spin ${ev.detail.spinIndex}`);
});
document.body.appendChild(container);
Control CSS styles
The package includes default CSS styling optimized for short-form content. You can override or extend these styles using standard CSS or CSS modules.
.fp-spins-container {
max-height: 100vh;
overflow: hidden;
background: #000;
}
flowplayer-spin {
border-radius: 10px;
}
Advanced usage
Configure with React
Spins also works seamlessly with React. Here's how you can embed it inside a React component:
import { createSpins } from "@flowplayer/spins";
import { useEffect, useRef } from "react";
function SpinsPlayer({ playlistId }: { playlistId: string }) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
const spinsContainer = createSpins({
playlist: playlistId,
token: "[your-player-token]",
lang: "en"
});
containerRef.current.appendChild(spinsContainer);
return () => {
if (containerRef.current.contains(spinsContainer)) {
containerRef.current.removeChild(spinsContainer);
}
};
}, [playlistId]);
return <div ref={containerRef} className="spins-container" />;
}