Give your users professional-grade caption editing without building it yourself. Our new IFrame Embed support lets you integrate our intuitive caption editor into your platform with minimal effort. Your users can edit, review, and correct captions without ever leaving your ecosystem, while you save valuable development time and resources.
Embed our editor on your website using an IFrame tag and a couple lines of code.
Embedding our caption editor is remarkably simple—just a few lines of code to get started. With support for over 30 different closed caption formats, we seamlessly integrate with virtually any captioning workflow. Whether you're handling standard SRT files or specialized broadcast formats, our editor has you covered.
Designed specifically for broadcast manufacturers and video platforms, our embedded editor fills the critical gap for services that support video captioning but lack editing capabilities. We're committed to your success with free technical support and training throughout the development process, ensuring a smooth implementation that meets your specific needs.

Build your own integration with Closed Caption Creator using our IFrame Remote.
Leverage Closed Caption Creator's incredible flexibility for all your captioning and timed-text metadata needs. This section provides the essential reference documentation—including setup guides, API details, and practical examples—to get your integration up and running fast.
The Closed Caption Creator IFrame Remote provides a powerful, embeddable subtitle and timed-text editor designed to seamlessly integrate into your web-based applications. Instead of building complex captioning functionalities from scratch, you can leverage our professional-grade editor to empower your users with advanced editing capabilities directly within your platform.
Our IFrame Remote is designed for secure and efficient cross-window communication using PenPal JS. This lightweight library facilitates reliable two-way communication between your parent application and the embedded editor, allowing you to:
This approach ensures a robust and flexible integration, enabling you to build highly interactive and customized captioning workflows.
To help you begin immediately, here are two essential links:
Dive into the Getting Started section below to begin your integration.
This section will walk you through the fundamental steps to embed the Closed Caption Creator IFrame Remote into your web application and establish communication for controlling the editor.
To follow along, you should have a basic understanding of HTML and JavaScript.
Import PenPal JS using NPM, or CDN.
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
<script src="https://unpkg.com/penpal@^7/dist/penpal.min.js"></script>
</body>
</html>
Begin by adding an element to your HTML where you want the editor to appear. Ensure it points to the Closed Caption Creator IFrame demo URL as its src (https://iframe-demo.closedcaptioncreator.com). Giving your iframe an id (e.g., editor) will make it easy to reference in JavaScript.
<div class="iframe-container">
<iframe id="editor"
src="https://iframe-demo.closedcaptioncreator.com/"
class="w-100 rounded"
style="height: 90vh;"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media"
allowfullscreen>
</iframe>
</div>
document.addEventListener('DOMContentLoaded', () => {
const iframe = document.getElementById('editor');
});
Once the IFrame is loaded, you'll establish a connection using PenPal. This connection provides a remote object that allows you to call functions exposed by the IFrame, and optionally, define methods on the parent window that the IFrame can call.
document.addEventListener('DOMContentLoaded', () => {
const iframe = document.getElementById('editor'); // Get reference to your iframe
let iframeConnection = null; // Will store the remote object for calling iframe functions
const connectToIframe = async () => {
try {
// Create a messenger to communicate with the iframe
const messenger = new Penpal.WindowMessenger({
remoteWindow: iframe.contentWindow,
allowedOrigins: ['*'] // IMPORTANT: For production, restrict this to your iframe's origin!
});
// Connect to the iframe
const connection = Penpal.connect({
messenger,
// Define methods the parent exposes to the iframe (optional)
methods: {
//...
}
});
// Wait for the connection to be established
const remote = await connection.promise;
console.log('Connected to iframe');
iframeConnection = remote; // Store the remote object for later use
// ... (initial editor configuration calls will go here)
} catch (error) {
console.error('Connection error:', error);
}
};
// Initialize connection when iframe loads
iframe.addEventListener('load', connectToIframe);
});
Once connected, you can immediately start interacting with the editor. A common first step is to configure basic project settings and load media/captions. This often involves showing a loading modal to the user.
await remote.updateModalTitle("Project Import");
await remote.toggleStatusModal(true); // Show the modal regardless of its current state
await remote.updateModalStatus({ // Update modal progress and message
progress : 5,
msg : "Setting project settings..."
});
/* Set Project Settings (framerate, dropFrame, incode) */
await remote.setProjectName("IFrame Demo Project");
await remote.setProjectDescription("This is a demo project for iframe communication.");
await remote.setProjectFrameRate(29.97);
await remote.setProjectDropFrame(true);
await remote.setProjectIncode("00:00:00:00");
/* Step 2. Loading Subtitle File */
await remote.updateModalStatus({
progress : 50,
msg : "Loading Subtitles..."
});
await remote.importSubtitle(subtitleUrl, subtitleProfile);
/* Step 3. Loading Video File */
await remote.updateModalStatus({
progress : 75,
msg : "Loading media..."
});
await remote.importMedia(mediaUrl, "https://m.media-amazon.com/images/M/MV5BMTljNGI3NTAtYTU2ZC00ZGQzLTg2ZDAtNzQ1NDk1YzBiY2M5XkEyXkFqcGc@._V1_QL75_UX522_.jpg");
/* Audio Peak Data */
await remote.setMediaPeaksPath(audioPeaksUrl);
await remote.toggleTimeline(true);
await remote.toggleStatusModal(false); // Hide the modal
await remote.alertUser({ // Show a success alert
title : "Project Import",
text : "Project imported successfully!",
});You now have the fundamental structure for embedding and communicating with the Closed Caption Creator IFrame Remote. Proceed to the Functions Reference to explore all available methods for deeper integration.
We are committed to ensuring your successful integration of the Closed Caption Creator IFrame Remote. If you encounter any questions during development, require personalized assistance, or have feedback on our documentation or product, please don't hesitate to reach out. We offer dedicated developer training and direct support to help with all aspects of your integration. Visit our Contact Page to connect with our team.
The Closed Caption Creator IFrame Remote exposes a set of asynchronous methods that your parent application can call via the PenPal connection. These methods allow you to control various aspects of the editor, from loading content to managing project settings and retrieving data.
All calls to these functions should be awaited, as they return Promises.
| Function Name | Arguments | Description | Return |
|---|---|---|---|
| updateModalStatus(statusInfo) | statusInfo: { progress: number, msg: string } | Updates the progress bar and status message in the IFrame remote modal. | Promise<boolean> |
| toggleStatusModal(show) | show: boolean | Shows or hides the IFrame remote status modal. If show is true, displays the modal; otherwise toggles its state. | Promise<void> |
| updateModalTitle(title) | title: string | Updates the title text displayed in the IFrame remote modal header. | Promise<boolean> |
| completeImport() | None | Displays a success alert and closes the modal after a successful import operation. | Promise<void> |
| failImport(msg) | msg: string | Displays an error alert with the provided message and closes the modal after a failed import. | Promise<void> |
| alertUser(alertObj) | alertObj: { title?: string, text?: string } | Displays a SweetAlert2 modal with a custom title and message to alert the user. | Promise<void> |
| setProjectId(id) | id: string | Sets the unique identifier for the current project. | Promise<void> |
| setProjectName(name) | name: string | Sets the display name of the current project. | Promise<void> |
| setProjectDescription(description) | description: string | Sets the description for the current project. | Promise<void> |
| setProjectFrameRate(frameRate) | frameRate: number (e.g., 23.976, 25, 29.97, 30) | Sets the project's video frame rate. | Promise<void> |
| setProjectDropFrame(dropFrame) | dropFrame: boolean | Sets whether the project uses drop-frame timecode. | Promise<void> |
| setProjectIncode(incode) | incode: string (timecode string, e.g., "00:00:00:00") | Sets the project's incode (start timecode). | Promise<void> |
| setProjectState(state) | state: object (Partial project state object) | Updates multiple project state properties at once. | Promise<void> |
| setDisplaySettings(settings) | settings: { displayWidth?: number (1-100), displayHeight?: number (1-50), xPadding?: number (0-50), yPadding?: number (0-50), lineSpacing?: number (0-50) } | Updates caption display settings including width, height, padding, and line spacing. Settings are validated and saved to localStorage. | Promise<{ success: boolean, message: string, settings?: object }> |
| setMediaPeaksPath(path) | path: string (URL to audio peaks JSON) | Sets the URL for the audio waveform peaks data to be displayed in the timeline. | Promise<void> |
| setMediaPeaksData(data) | data: object (Audio peaks data object) | Directly sets the audio waveform peaks data as an object, bypassing a URL fetch. | Promise<void> |
| useFallbackTimeline() | None | Instructs the timeline to use a fallback visual representation if peaks data is unavailable or problematic. | Promise<void> |
| useDefaultTimeline() | None | Ensures the timeline attempts to use provided peaks data rather than a fallback. | Promise<void> |
| toggleTimeline(enable) | enable: boolean | Toggles the visibility of the timeline UI element. If enable is true, shows the timeline. If false, toggles its current state. | Promise<void> |
| setPoster(thumbnailUrl) | thumbnailUrl: string | Sets the poster image (thumbnail) for the video player. | Promise<void> |
| importMedia(mediaUrl, thumbnailUrl, mediaType, storageType) | mediaUrl: string thumbnailUrl: string (Optional) mediaType: string (e.g., "video/mp4", default "video/mp4") storageType: string (e.g., "Cloud Storage", "Vimeo", "YouTube", "HLS Manifest", default "Cloud Storage") |
Loads a video or audio file into the editor from a specified URL. | Promise<{ success: boolean, message: string }> |
| setTranscript(transcript, profile) | transcript: object (Transcription data object) profile: string (Source profile, e.g., "deepgram", default "deepgram") |
Sets the raw transcript data to be processed by the editor. | Promise<void> |
| loadTranscript() | None | Processes the transcript data previously set via setTranscript and imports it into the editor as a new event group. | Promise<{ success: boolean, message: string }> |
| importSubtitle(subtitleUrl, profile, decodeOptions, evgOptions, autoFormatOptions) | subtitleUrl: string (URL to subtitle file) profile: string (Subtitle format profile, e.g., "subRip", "webVtt") decodeOptions: object (Optional, format-specific decoding options) evgOptions: object (Optional, event group options like type, name, language) autoFormatOptions: { enable?: boolean, maxLines?: number, maxChars?: number, minDuration?: number, allowOrphanWords?: boolean } (Optional) |
Loads a subtitle file from a URL into the editor, converting it to the internal project format. Optionally applies auto-formatting. | Promise<{ success: boolean, message: string }> |
| exportSubtitle(profile, encodeOptions, saveAsFile) | profile: string (Target subtitle format profile) encodeOptions: object (Optional, format-specific encoding options) saveAsFile: boolean (If true, triggers a file download; otherwise, returns data) |
Exports the current captions from the editor into a specified format. Can return the data or trigger a file download. | Promise<{ success: boolean, message: string, data?: string, fileName?: string }> |
| getMarkers() | None | Retrieves the current marker data from the editor, including all marker lists and their markers. | Promise<object> (Copy of marker state) |
| setMarkers(markers) | markers: object (Marker state object with lists and selected properties) | Sets the entire marker state for the editor, allowing you to load custom markers and lists. | Promise<void> |
| createMarkerList(markerList) | markerList: { name: string, color?: string } | Creates a new named marker list within the editor. | Promise<void> |
| insertMarker(marker, markerListId) | marker: object (_Marker object, e.g., { time: number, comment: string }) markerListId: number (Optional, ID of the target marker list, default 0) |
Inserts a new marker into a specified marker list. | Promise<void> |
| loadStyleGuide(styleGuideData) | styleGuideData: object (Style guide configuration object) | Loads or updates a style guide in the editor's local storage. If a guide with the same ID exists, it will be updated. | Promise<{ success: boolean, message: string, id?: string }> |
| getStyleGuides() | None | Retrieves all style guides currently stored in local storage. | Promise<{ success: boolean, data: array, count: number, message: string }> |
| qcEventGroup(eventGroupId, styleGuideId) | eventGroupId: string (ID of event group to validate) styleGuideId: string (ID of style guide to use for validation) |
Runs quality control validation on a specified event group using a specified style guide. Returns all validation errors found. | Promise<{ success: boolean, errors: array, errorCount: number, eventGroupId: string, eventGroupName: string, styleGuideId: string, styleGuideName: string, message: string }> |
| getSelectedEventGroupId() | None | Retrieves the ID and details of the currently selected event group in the editor. | Promise<{ success: boolean, id: string|null, index: number|null, name?: string, type?: string, eventCount?: number, message: string }> |
| loadProjectData(ccprj) | ccprj: object or string (Closed Caption Project JSON data) | Loads a complete Closed Caption Creator project (in .ccprj format) into the editor. | Promise<{ success: boolean, message: string }> |
| getProjectData() | None | Retrieves the entire current project data from the editor in .ccprj JSON format. | Promise<object> (Complete project JSON) |
| getSelectedEvents() | None | Retrieves all currently selected events from the active event group. | Promise<array> (Array of selected event objects) |
| selectEventById(id) | id: string (Event ID) | Selects a specific event in the current event group by its unique ID. | Promise<void> |
| selectEventByIndex(index) | index: number | array (Event index or array of indices) | Selects one or more events in the current event group by their index position(s). Accepts a single index or an array of indices for multiple selection. | Promise<void> |
| scrollToEventById(id) | id: string (Event ID) | Scrolls the event list to make the event with the specified ID visible in the editor. | Promise<void> |
| scrollToEventByIndex(index) | index: number (Event index) | Scrolls the event list to make the event at the specified index visible in the editor. | Promise<void> |
A core functionality of the Closed Caption Creator IFrame Remote is the ability to seamlessly import existing subtitle files and export edited captions into various formats. This section details the methods and key parameters involved in these crucial operations.
To load subtitles into the editor, you will use the importSubtitle method. This method is designed to handle a wide array of caption formats and allows for granular control over how the data is interpreted.
importSubtitle(subtitleUrl, profile, decodeOptions, eventGroupOptions)
To retrieve the edited captions from the editor, you will use the exportSubtitle method. This allows you to generate output in your desired target format.
exportSubtitle(profile, encodeOptions, saveAsFile)
One of the most powerful features of the Closed Caption Creator IFrame Remote is the ability to implement sophisticated quality control (QC) and review workflows directly within your application. By leveraging custom style guides, you can enforce caption quality standards, catch common errors, and ensure that all subtitles meet your specific requirements before they're finalized or published.
This section introduces the QC capabilities available through the IFrame Embed solution, with a focus on custom style guides and how to integrate validation checks into your approval workflows.
Custom style guides are configurable rulesets that define quality standards and formatting requirements for your captions. These guides can check for a wide range of issues, including:
Style guides provide flexibility for different use cases—whether you're creating captions for broadcast television, online streaming platforms, educational content, or accessibility compliance. Each use case may have unique requirements, and custom style guides allow you to enforce those standards programmatically.
Style guides can be created and loaded in two primary ways:
Users can create their own style guides directly within the Closed Caption Creator interface, defining rules that match their specific workflow needs. These guides are saved to the user's account and can be applied to any project.
For more advanced integrations, your parent application can dynamically create and load style guides based on the specific project, client requirements, or platform standards. This approach is ideal when different projects require different validation rules, or when you want to maintain centralized control over caption quality standards across your platform.
The loadStyleGuide function enables you to programmatically load a style guide into the editor, allowing for automated QC workflows.
Below is the JSON schema that defines the structure of a custom style guide. This schema allows you to specify the rules and thresholds that will be applied during validation:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Style Guide Configuration",
"description": "Configuration object for caption/subtitle style guide validation rules",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the style guide (UUID v4 format)",
"example": "550e8400-e29b-41d4-a716-446655440000"
},
"name": {
"type": "string",
"description": "Display name for the style guide",
"default": "Untitled Style Guide",
"example": "Netflix Style Guide"
},
"enabled": {
"type": "boolean",
"description": "Whether this style guide is currently active for validation",
"default": true
},
"totalMaxChars": {
"type": "number",
"description": "Maximum total characters allowed across all lines in a single caption event",
"default": 9999,
"minimum": 0
},
"minLines": {
"type": "number",
"description": "Minimum number of lines allowed per caption event",
"default": 1,
"minimum": 1
},
"maxLines": {
"type": "number",
"description": "Maximum number of lines allowed per caption event",
"default": 4,
"minimum": 1
},
"maxChars": {
"type": "number",
"description": "Maximum characters allowed per line",
"default": 32,
"minimum": 1
},
"maxDuration": {
"type": "number",
"description": "Maximum duration (in seconds) a caption event can be displayed",
"default": 6,
"minimum": 0
},
"minDuration": {
"type": "number",
"description": "Minimum duration (in seconds) a caption event must be displayed",
"default": 0.2,
"minimum": 0
},
"minCps": {
"type": "number",
"description": "Minimum characters per second reading speed",
"default": 0,
"minimum": 0
},
"maxCps": {
"type": "number",
"description": "Maximum characters per second reading speed (typically 15-20 for comfortable reading)",
"default": 9999,
"minimum": 0
},
"minWpm": {
"type": "number",
"description": "Minimum words per minute reading speed",
"default": 0,
"minimum": 0
},
"maxWpm": {
"type": "number",
"description": "Maximum words per minute reading speed",
"default": 9999,
"minimum": 0
},
"minWordsPerLine": {
"type": "number",
"description": "Minimum number of words allowed per line",
"default": 1,
"minimum": 1
},
"maxWordsPerLine": {
"type": "number",
"description": "Maximum number of words allowed per line",
"default": 10,
"minimum": 1
},
"minEventGap": {
"type": "number",
"description": "Minimum gap (in seconds) required between consecutive caption events",
"default": 0,
"minimum": 0
},
"maxEventGap": {
"type": "number",
"description": "Maximum gap (in seconds) allowed between consecutive caption events",
"default": 6,
"minimum": 0
},
"minEventGapTolerance": {
"type": "number",
"description": "Tolerance threshold (in seconds) for minimum event gap violations",
"default": 0,
"minimum": 0
},
"maxEventGapTolerance": {
"type": "number",
"description": "Tolerance threshold (in seconds) for maximum event gap violations",
"default": 6,
"minimum": 0
},
"overlap": {
"type": "boolean",
"description": "Flag to detect overlapping caption events (when enabled, overlaps are flagged as violations)",
"default": true
},
"illegalChars": {
"type": "boolean",
"description": "Flag to detect illegal or unsupported characters in captions",
"default": false
},
"hyphenSpace": {
"type": "boolean",
"description": "Flag to detect improper spacing around hyphens",
"default": false
},
"hasUnderscore": {
"type": "boolean",
"description": "Flag to detect underscores in caption text (often not allowed)",
"default": false
},
"periods": {
"type": "boolean",
"description": "Flag to validate period usage in captions",
"default": false
},
"missingSpeaker": {
"type": "boolean",
"description": "Flag to detect captions missing speaker identification",
"default": false
},
"useEllipses": {
"type": "boolean",
"description": "Flag to validate ellipses usage in captions",
"default": false
},
"spellNumbers": {
"type": "boolean",
"description": "Flag to require numbers to be spelled out as words",
"default": false
},
"spellNumbersAtStart": {
"type": "boolean",
"description": "Flag to require numbers at the start of sentences to be spelled out",
"default": false
},
"netflixGlyphs": {
"type": "boolean",
"description": "Flag to validate Netflix-specific glyph requirements",
"default": false
},
"partialItalics": {
"type": "boolean",
"description": "Flag to detect partial italic formatting within a caption event",
"default": false
},
"fullItalics": {
"type": "boolean",
"description": "Flag to detect fully italicized caption events",
"default": false
},
"partialBold": {
"type": "boolean",
"description": "Flag to detect partial bold formatting within a caption event",
"default": false
},
"fullBold": {
"type": "boolean",
"description": "Flag to detect fully bolded caption events",
"default": false
},
"partialUnderline": {
"type": "boolean",
"description": "Flag to detect partial underline formatting within a caption event",
"default": false
},
"fullUnderline": {
"type": "boolean",
"description": "Flag to detect fully underlined caption events",
"default": false
},
"repeatWords": {
"type": "boolean",
"description": "Flag to detect repeated words in caption text",
"default": false
},
"fitSubtitles": {
"type": "boolean",
"description": "Flag to require all subtitles to fit on a single row",
"default": false
},
"leadingTrailingSpace": {
"type": "boolean",
"description": "Flag to detect leading or trailing whitespace in caption text",
"default": false
},
"whitespace": {
"type": "boolean",
"description": "Flag to detect excessive or improper whitespace in captions",
"default": false
},
"blankLines": {
"type": "boolean",
"description": "Flag to detect blank lines within caption events",
"default": false
},
"positionTopLeft": {
"type": "boolean",
"description": "Flag to validate captions positioned in the top-left area",
"default": false
},
"positionTopCenter": {
"type": "boolean",
"description": "Flag to validate captions positioned in the top-center area",
"default": false
},
"positionTopRight": {
"type": "boolean",
"description": "Flag to validate captions positioned in the top-right area",
"default": false
},
"positionCenterLeft": {
"type": "boolean",
"description": "Flag to validate captions positioned in the center-left area",
"default": false
},
"positionCenter": {
"type": "boolean",
"description": "Flag to validate captions positioned in the center area",
"default": false
},
"positionCenterRight": {
"type": "boolean",
"description": "Flag to validate captions positioned in the center-right area",
"default": false
},
"positionBottomLeft": {
"type": "boolean",
"description": "Flag to validate captions positioned in the bottom-left area",
"default": false
},
"positionBottomCenter": {
"type": "boolean",
"description": "Flag to validate captions positioned in the bottom-center area",
"default": false
},
"positionBottomRight": {
"type": "boolean",
"description": "Flag to validate captions positioned in the bottom-right area",
"default": false
},
"positionYOffset": {
"type": "boolean",
"description": "Flag to validate vertical position offset values",
"default": false
},
"positionXOffset": {
"type": "boolean",
"description": "Flag to validate horizontal position offset values",
"default": false
},
"approvalPassed": {
"type": "boolean",
"description": "Flag to filter/validate captions marked as approved",
"default": false
},
"approvalFailed": {
"type": "boolean",
"description": "Flag to filter/validate captions marked as failed approval",
"default": false
},
"approvalNotSet": {
"type": "boolean",
"description": "Flag to filter/validate captions with no approval status set",
"default": false
},
"notes": {
"type": "boolean",
"description": "Flag to validate presence of notes/annotations on captions",
"default": false
},
"tags": {
"type": "boolean",
"description": "Flag to validate presence of tags on captions",
"default": false
},
"forced": {
"type": "boolean",
"description": "Flag to validate forced narrative captions (non-dialogue captions)",
"default": false
}
},
"examples": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Netflix Standard",
"enabled": true,
"totalMaxChars": 84,
"minLines": 1,
"maxLines": 2,
"maxChars": 42,
"maxDuration": 7,
"minDuration": 0.833,
"minCps": 0,
"maxCps": 20,
"minWpm": 0,
"maxWpm": 9999,
"minWordsPerLine": 1,
"maxWordsPerLine": 10,
"minEventGap": 0.083,
"maxEventGap": 6,
"minEventGapTolerance": 0,
"maxEventGapTolerance": 6,
"overlap": true,
"netflixGlyphs": true,
"leadingTrailingSpace": true,
"whitespace": true
},
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "BBC Standard",
"enabled": true,
"totalMaxChars": 9999,
"minLines": 1,
"maxLines": 2,
"maxChars": 37,
"maxDuration": 6,
"minDuration": 0.3,
"minCps": 0,
"maxCps": 17,
"minWpm": 0,
"maxWpm": 200,
"minWordsPerLine": 1,
"maxWordsPerLine": 10,
"minEventGap": 0,
"maxEventGap": 6,
"overlap": true,
"illegalChars": true,
"leadingTrailingSpace": true
}
]
}
Once a style guide is loaded, the user or the parent application can trigger validation checks and retrieve any errors or warnings that are found. This enables you to build sophisticated approval workflows where captions must pass QC validation before they can be published or saved.
A typical QC workflow involves these steps:
This approach gives you complete control over when and how quality checks are performed, and allows you to integrate caption validation seamlessly into your existing content approval pipelines.
Create closed captioning and subtitles for your broadcast videos using Closed Caption Creator. Create, edit, and review closed captioning using our intuitive timed-text editor.
Learn MoreTranslate and localize closed captioning, and subtitles in our dedicated Translation UI. View the original source language alongside the translated text to ensure an accurate translation.
Learn MoreCreate as-broadcast scripts, and dialogue lists with custom notes, tags, Speaker IDs, and more using Closed Caption Creator. Support for Microsoft Word (.docx), CSV, and plaintext formats.
Learn MoreCreate lifelike audio descriptions and described video (DV) using the power of synthetic voice. Unlock the ability to create closed captioning and audio descriptions all in one application.
Learn MorePackage broadcast-ready files with the CC Embed export option for Closed Caption Creator. Supports MXF, MP4, MOV, and more.
Learn MoreAutomate your closed caption and subtitle workflows using our dedicated Closed Caption Converter API. Support over 30 different closed caption and subtitle file formats.
Learn More