Creating interactive video experiences in Wistia allows developers to enhance viewer engagement through features like clickable CTAs, in-video forms, and branching logic. By utilizing Wistia"s APIs, embed options, and JavaScript hooks, developers can build dynamic, data-driven video workflows that provide a seamless and responsive user experience.
Overview of Interactive Video Capabilities in Wistia
Wistia has evolved from a simple video hosting solution into a sophisticated platform for interactive video experiences, empowering marketers, educators, and developers to create dynamic, viewer-driven content. Through a combination of intuitive Studio tools and flexible APIs, Wistia enables seamless integration of interactive elements. These features are not only designed to enhance engagement but are also fully trackable through detailed event binding.
Clickable CTAs & Hotspots
Wistia"s platform allows you to embed clickable Call-to-Actions (CTAs) and hotspots directly within your videos. These interactive elements can be placed at specific moments, encouraging viewers to take action, such as visiting a webpage, signing up for a service, or making a purchase. These CTAs can be easily configured and customized to fit the video's flow and purpose, and they are fully trackable, so you can monitor their effectiveness.
In-Video Forms & Surveys
Wistia also supports the integration of in-video forms and surveys. These interactive forms can appear at any point during the video, allowing you to gather viewer information, feedback, or lead data without interrupting the viewing experience. Whether it"s collecting email addresses, conducting polls, or obtaining survey responses, these tools provide a way to collect valuable data directly from your audience within the video.
Branching Logic Based on Viewer Choices
Branching logic allows you to create customized viewing paths based on the viewer's actions. Using this feature, developers can set up different video outcomes depending on what a viewer clicks or chooses during the video. For example, a viewer might be given options to choose a product category, leading them to a video segment tailored to their choice. This creates a more personalized, interactive experience, increasing viewer engagement.
Navigation Menus for Seamless Exploration
Wistia"s navigation menus give viewers the ability to explore different parts of the video at their own pace. These menus can be embedded within the video player, allowing users to jump to specific sections or chapters without needing to scrub through the video manually. This feature enhances the viewing experience, especially for longer videos or tutorials, by providing quick access to relevant content.
Step 1: Embed a Wistia Video
Upload your video to Wistia, then embed it on your site with the asynchronous iframe method. This exposes the video player and enables programmatic control via Wistia.api.
Example:
<div class="wistia_responsive_padding" style="padding:56.25% 0 0 0;position:relative;">
<div class="wistia_responsive_wrapper" style="height:100%;left:0;position:absolute;top:0;width:100%;">
<div class="wistia_embed wistia_async_VIDEO_ID" id="wistia_video"></div>
</div>
</div>
<script src="https://fast.wistia.com/assets/external/E-v1.js" async></script>Replace VIDEO_ID with your actual Wistia video ID. This initializes the video player and exposes the Wistia.api object for programmatic control.
Explanation:
- <div class="wistia_responsive_padding">: This div serves as the wrapper for the Wistia player, providing responsive padding to maintain the video's aspect ratio.
- style="padding:56.25% 0 0 0;position:relative;": The inline style achieves a 16:9 aspect ratio (56.25% is 9/16, which is the standard aspect ratio for videos).
- padding:56.25%: The padding-top is set to 56.25% to ensure that the container maintains the correct 16:9 aspect ratio regardless of the container"s width.
- position:relative;: This positions the element relative to its normal position, which is necessary to contain the positioned inner elements.
- <div class="wistia_responsive_wrapper">: This div is another wrapper around the actual video player that ensures it stretches to fit the container dimensions.
Step 2: Initialize the Player and Listen for Events
Use the _wq queue to detect when the video is ready, then bind documented events like ctaclick and ctaformsubmit to handle user interactions programmatically.
Example:
window._wq = window._wq || [];
_wq.push({
id: "VIDEO_ID",
onReady: function(video) {
console.log("Wistia video is ready");
video.bind("ctaclick", function(ctaData) {
console.log("CTA Clicked:", ctaData);
});
video.bind("ctaformsubmit", function(formData) {
console.log("Form Submitted:", formData);
// Example: Send data to analytics
fetch('/track-form-submit', {
method: 'POST',
body: JSON.stringify(formData),
headers: { 'Content-Type': 'application/json' }
});
});
}
});Explanation:
- window._wq: This line ensures that the global _wq object exists on the window (global) scope. _wq stands for Wistia Queue.
- window._wq || []: The || [] ensures that if _wq does not already exist (i.e., it"s not initialized yet), it will be set to an empty array.
- _wq.push({...}): This pushes an object to the _wq queue. Each object in the queue specifies an event handler for a specific Wistia video.
- id: "VIDEO_ID": This refers to the unique identifier of the Wistia video you are targeting. Replace "VIDEO_ID" with the actual Wistia video ID.
- onReady: function(video) { ... }: The onReady function is called when the video player has fully loaded and is ready to interact with.
Step 3: Implement Branching Logic and State Persistence
Branching is achieved by pausing playback at key timestamps, displaying choice UI, and jumping to different video segments based on user input, with state saved in localStorage for persistence.
Track Branch Choices and Persist Viewer State
Track and save a viewer"s branch choice by storing the state in local storage, allowing the video to remember the viewer's choice even after page refreshes.
Example:
window._wq = window._wq || [];
_wq.push({
id: "VIDEO_ID",
onReady: function(video) {
video.bind("secondchange", function(s) {
if (s === 45 && branchState === 'default') {
video.pause();
document.getElementById("branching-options").style.display = 'block';
}
});
// Handle user branch selection
document.getElementById("choose-path-a").addEventListener('click', function() {
branchState = 'pathA';
localStorage.setItem('videoBranch', branchState);
document.getElementById("branching-options").style.display = 'none';
video.time(60); // Jump to segment A
video.play();
});
document.getElementById("choose-path-b").addEventListener('click', function() {
branchState = 'pathB';
localStorage.setItem('videoBranch', branchState);
document.getElementById("branching-options").style.display = 'none';
video.time(90); // Jump to segment B
video.play();
});
}
});Explanation:
- window._wq: This initializes the Wistia Queue (_wq) if it doesn't already exist. It is an array used to store event handlers that will be executed when the Wistia video is ready.
- window._wq || []: The || [] ensures that if _wq is not defined yet (for example, if this script is run before the Wistia API is loaded), it will be initialized as an empty array.
- _wq.push({...}): This pushes an object containing the event handler into the _wq array. The object specifies what should happen when the video is ready.
- id: "VIDEO_ID": This is the unique identifier for the Wistia video. Replace "VIDEO_ID" with the actual ID of your video.
- onReady: function(video): The onReady function is called once the video has finished loading and is ready for interaction.
Step 4: Create Hotspots and Custom Interactions
Since hotspots aren"t exposed via Wistia"s public API, simulate them with HTML overlays that toggle visibility based on the video"s current time using the timechange event. Wistia"s interactivity UI does not expose hotspot creation via public API, but you can simulate hotspots using transparent overlays or DOM elements synced with video time.
Example:
_wq.push({
id: "VIDEO_ID",
onReady: function(video) {
video.bind("timechange", function() {
if (video.time() >= 30 && video.time() < 35) {
document.getElementById("hotspot").style.display = 'block';
} else {
document.getElementById("hotspot").style.display = 'none';
}
});
}
});
<div id="hotspot" style="
display:none;
position:absolute;
top:20%;
left:30%;
width:100px;
height:50px;
background-color:rgba(255,255,255,0.3);
cursor:pointer;
" onclick="handleHotspotClick()"></div>Explanation:
- video.bind("timechange", function() {...}): This binds to the timechange event, which is fired whenever the video"s playback time changes.
- Hotspot: shown when the video reaches the 30-second mark and disappears after 35 seconds.
Step 5: Debugging and Developer Workflow Tips
Leverage the global _wq queue to inspect and access player instances for debugging. Ensure interactive elements support touch events and optimize event binding when handling multiple players.
Use _wq for Dynamic Debugging
Use the _wq variable to dynamically inspect and debug live video behavior, allowing for real-time testing and troubleshooting.
Example:
console.log(window._wq);Or add a dynamic queue handler during development:
_wq.push({ id: "VIDEO_ID", onReady: function(video) { window.wistiaVideo = video; console.log('Wistia player ready:', video); } });Explanation:
- console.log(window._wq);: Logs the _wq array, which stores event handlers for Wistia videos. This lets developers inspect the current state of the queue.
- _wq.push({...}): Pushes an object into the _wq array, specifying the event handler for a particular video.
- onReady: function(video) {...}: The onReady function is triggered once the video player has finished loading. Inside this function, you can manipulate the video object.
- window.wistiaVideo = video;: Assigns the video instance to a global variable so it can be accessed throughout the developer"s console or debugging environment.
- console.log('Wistia player ready:', video);: Logs the video object when it is ready, allowing you to inspect its properties and methods in the console.
Mobile Support & Touch Events
Make sure all interactive overlays are optimized for touch devices, ensuring smooth usability on mobile.
Example:
#hotspot { touch-action: manipulation; pointer-events: auto; }Explanation:
- touch-action: manipulation;: This CSS property is used to disable certain default touch behaviors (like pinch-to-zoom) on mobile devices.
- pointer-events: auto;: This ensures that the hotspot element is interactive, i.e., the element will respond to user pointer events (like tapping on mobile or mouse clicks on desktop).
Optimize Performance for Multiple Videos
To optimize performance when managing multiple Wistia players, avoid reinitializing listeners on the same ID to prevent redundant operations. Lazy-load inactive video embeds to improve page load times and resource usage. Additionally, use namespaces for video.bind() callbacks to prevent memory leaks and ensure efficient event handling.
Step 6: Analytics and Engagement Tracking
Now you need to bind to specific Wistia events like ctaclick and ctaformsubmit to capture user actions. After that, you must forward this data to external analytics services via standard HTTP requests.
Example:
_wq.push({
id: "VIDEO_ID",
onReady: function(video) {
video.bind("ctaclick", function(data) {
// Push to custom analytics
sendToAnalytics("cta_click", data);
});
video.bind("ctaformsubmit", function(data) {
sendToAnalytics("form_submitted", data);
});
}
});
function sendToAnalytics(eventType, eventData) {
fetch('/track', {
method: 'POST',
body: JSON.stringify({ type: eventType, data: eventData }),
headers: { 'Content-Type': 'application/json' }
});
}Explanation:
- _wq.push({...}): This adds a new event handler to the _wq array for a specific video. When the video with id="VIDEO_ID" is ready, the functions inside onReady are executed.
- video.bind("ctaclick", function(data) {...}): This binds the ctaclick event to the video player. When the viewer clicks on a CTA during the video, the data associated with that click is passed to the callback function.
- video.bind("ctaformsubmit", function(data) {...}): Similarly, the ctaformsubmit event is bound to track when a viewer submits a form embedded in the video.
- sendToAnalytics(eventType, eventData): This function sends the event data to an external server. The fetch() API is used to send a POST request to the /track endpoint.
- fetch('/track', {...}): Sends a POST request to the server to log the event. The request body contains the eventType and the eventData.

