Examples of how to embed videos from play.3speak.tv
Videos with available translations show a CC button in the control bar. Click it to select a language. The last selected language is remembered across sessions.
// URL Parameters for Captions:
&captions=0 // Disable captions entirely (no CC button)
// Default: captions enabled, CC button shown if subtitles available
// postMessage API:
send('setCaptions', { lang: 'en' }); // Enable English captions
send('setCaptions', { lang: null }); // Disable captions
send('getCaptionLanguages'); // Get available languages
// localStorage persistence:
// 3speak-subtitle-lang → last selected language (auto-selects next video)
This is the recommended way to embed videos. The &mode=iframe parameter hides the header and info panel for a clean, embeddable experience.
<iframe
src="https://play.3speak.tv/watch?v=meno/1czchhmr&mode=iframe"
width="854"
height="480"
frameborder="0"
allowfullscreen>
</iframe>
This maintains a 16:9 aspect ratio and adapts to any screen size.
<div style="position: relative; padding-bottom: 56.25%; height: 0;">
<iframe
src="https://play.3speak.tv/watch?v=meno/1czchhmr&mode=iframe"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
frameborder="0"
allowfullscreen>
</iframe>
</div>
Compare the difference between full player page and minimal embed mode.
?v=meno/1czchhmr
?v=meno/1czchhmr&mode=iframe
You can customize the size to fit your layout.
The 3speak player supports three layout modes to handle different embedding scenarios. Choose the one that fits your use case:
| Mode | Aspect Ratio | Best For | Key Feature |
|---|---|---|---|
layout=mobile |
3:4 (tall) | Mobile apps, social feeds | Universal container for all videos |
layout=square |
1:1 (square) | Grid layouts, thumbnails | Maximum compatibility |
layout=desktop |
16:9 (YouTube-style) | Web embeds, blog posts | Professional letterboxing |
The &layout=desktop mode maintains a strict 16:9 aspect ratio like YouTube. All videos (vertical, horizontal, or square) display with perfect letterboxing. This is perfect for web embeds.
&layout=desktop
/* All vertical videos get black bars on sides */
&layout=desktop
/* Horizontal videos fill the container naturally */
Recommended for mobile apps! The &layout=mobile parameter creates a tall 3:4 container that works beautifully for both vertical AND horizontal videos without any scrollbars.
/* Perfect fit for vertical videos */
&layout=mobile
height="600"
/* Horizontal gets black bars above/below */
&layout=mobile
height="600"
For maximum universal compatibility, use &layout=square for a 1:1 square container. Works for any video orientation.
<iframe
src="https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=square"
width="350"
height="350"
frameborder="0"
allowfullscreen>
</iframe>
For web applications, you don't need to manually detect video orientation. Just listen for the player's ready message and let it tell you!
// ONE TIME setup - works for ALL videos on your page
window.addEventListener('message', function(event) {
if (event.data.type === '3speak-player-ready') {
const iframe = event.source.frameElement;
// Player tells you video dimensions and orientation!
console.log('Video info:', {
isVertical: event.data.isVertical, // true/false
width: event.data.width, // e.g., 720
height: event.data.height, // e.g., 1280
aspectRatio: event.data.aspectRatio // e.g., 0.5625
});
// Adjust your iframe or container based on orientation
if (event.data.isVertical) {
iframe.height = '800px';
iframe.maxWidth = '450px';
} else {
iframe.height = '450px';
iframe.maxWidth = '800px';
}
}
});
// Embed videos normally - no special code per video!
function embedVideo(author, permlink) {
const iframe = document.createElement('iframe');
iframe.src = `https://play.3speak.tv/embed?v=${author}/${permlink}&mode=iframe`;
iframe.width = '100%';
iframe.height = '600'; // Default, will auto-adjust
iframe.frameBorder = '0';
iframe.allowFullscreen = true;
document.body.appendChild(iframe);
}
import { useEffect, useRef, useState } from 'react';
const ThreeSpeakPlayer = ({ author, permlink }) => {
const iframeRef = useRef(null);
const [isVertical, setIsVertical] = useState(false);
useEffect(() => {
const handleMessage = (event) => {
if (event.data.type === '3speak-player-ready' &&
event.source === iframeRef.current?.contentWindow) {
setIsVertical(event.data.isVertical);
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);
const height = isVertical ? '800px' : '450px';
const maxWidth = isVertical ? '450px' : '800px';
return (
<div style={{ maxWidth, margin: '0 auto' }}>
<iframe
ref={iframeRef}
src={`https://play.3speak.tv/embed?v=${author}/${permlink}&mode=iframe`}
width="100%"
height={height}
style={{ border: 'none', borderRadius: '8px' }}
allowFullScreen
/>
</div>
);
};
// Usage
<ThreeSpeakPlayer author="vempromundo" permlink="hkh2vzzf" />
Problem: Mobile apps can't always adapt iframe sizes dynamically. Solution: Use the &layout=mobile parameter for universal containers that work with any video orientation!
Perfect for all orientations
Universal compatibility
Default adaptive behavior
The &layout=mobile parameter creates a 3:4 tall container perfect for mobile apps. Works beautifully for both vertical AND horizontal videos without any scrollbars!
/* Perfect for vertical videos */
&layout=mobile
/* Also works for horizontal - letterboxed */
&layout=mobile
For apps that need a completely universal container, &layout=square creates a 1:1 square that works for any video orientation.
<iframe
src="https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=square"
width="350"
height="350"
frameborder="0"
allowfullscreen>
</iframe>
import UIKit
import WebKit
class VideoPlayerViewController: UIViewController {
var webView: WKWebView!
let author: String
let permlink: String
init(author: String, permlink: String) {
self.author = author
self.permlink = permlink
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Create WKWebView
webView = WKWebView(frame: self.view.bounds)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.scrollView.isScrollEnabled = false // No scrollbars!
view.addSubview(webView)
// 🎯 Use layout=mobile for universal compatibility!
let videoURL = "https://play.3speak.tv/embed?v=\(author)/\(permlink)&mode=iframe&layout=mobile"
if let url = URL(string: videoURL) {
let request = URLRequest(url: url)
webView.load(request)
}
}
}
// Usage - Same code for vertical AND horizontal videos!
let verticalVideo = VideoPlayerViewController(author: "vempromundo", permlink: "hkh2vzzf")
let horizontalVideo = VideoPlayerViewController(author: "meno", permlink: "1czchhmr")
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
class VideoPlayerActivity : AppCompatActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_video_player)
val author = intent.getStringExtra("author") ?: ""
val permlink = intent.getStringExtra("permlink") ?: ""
webView = findViewById(R.id.webView)
webView.settings.javaScriptEnabled = true
webView.settings.mediaPlaybackRequiresUserGesture = false
webView.webViewClient = WebViewClient()
// 🎯 Use layout=mobile - works for all orientations!
val videoUrl = "https://play.3speak.tv/embed?v=$author/$permlink&mode=iframe&layout=mobile"
webView.loadUrl(videoUrl)
}
}
// XML Layout (activity_video_player.xml)
/*
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
*/
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { WebView } from 'react-native-webview';
const ThreeSpeakPlayer = ({ author, permlink, style }) => {
// 🎯 Use layout=mobile for universal compatibility
const videoUrl = `https://play.3speak.tv/embed?v=${author}/${permlink}&mode=iframe&layout=mobile`;
return (
<View style={[styles.container, style]}>
<WebView
source={{ uri: videoUrl }}
allowsFullscreenVideo={true}
mediaPlaybackRequiresUserAction={false}
javaScriptEnabled={true}
scrollEnabled={false}
style={styles.webview}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#000',
aspectRatio: 3/4, // Match mobile layout ratio
},
webview: {
backgroundColor: 'transparent',
}
});
// Usage - Same component for ALL videos!
export default function App() {
return (
<View style={{ flex: 1 }}>
<ThreeSpeakPlayer author="vempromundo" permlink="hkh2vzzf" />
<ThreeSpeakPlayer author="meno" permlink="1czchhmr" />
</View>
);
}
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class ThreeSpeakPlayer extends StatefulWidget {
final String author;
final String permlink;
const ThreeSpeakPlayer({
Key? key,
required this.author,
required this.permlink,
}) : super(key: key);
@override
State<ThreeSpeakPlayer> createState() => _ThreeSpeakPlayerState();
}
class _ThreeSpeakPlayerState extends State<ThreeSpeakPlayer> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
// 🎯 Use layout=mobile for universal compatibility
final videoUrl = 'https://play.3speak.tv/embed?v=${widget.author}/${widget.permlink}&mode=iframe&layout=mobile';
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.parse(videoUrl));
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
child: AspectRatio(
aspectRatio: 3 / 4, // Match mobile layout ratio
child: WebViewWidget(controller: _controller),
),
);
}
}
// Usage in your feed
ListView.builder(
itemCount: videos.length,
itemBuilder: (context, index) {
final video = videos[index];
return ThreeSpeakPlayer(
author: video.author,
permlink: video.permlink,
);
},
)
&layout=mobile to your iframe URL and you're done!
// MOBILE LAYOUT - Tall container (3:4 aspect ratio) - RECOMMENDED FOR APPS
https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=mobile
// Perfect for: iOS, Android, React Native, Flutter apps
// SQUARE LAYOUT - Universal square container (1:1)
https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=square
// Perfect for: Social media grids, thumbnails, ultra-simple layouts
// DESKTOP LAYOUT - Flexible responsive (default behavior)
https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=desktop
// Perfect for: Web apps that can dynamically resize iframes
// NO LAYOUT PARAMETER - Same as desktop (fluid responsive)
https://play.3speak.tv/embed?v=author/permlink&mode=iframe
// Uses default fluid mode with PostMessage API for dimension communication
noscroll Parameter&noscroll=1 to completely disable all scrollbars within the player iframe.
// Standard embed - may show scrollbars in tight layouts
https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=mobile
// No-scroll mode - guaranteed zero scrollbars
https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=mobile&noscroll=1
// Works with any layout mode
https://play.3speak.tv/embed?v=author/permlink&mode=iframe&noscroll=1
https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=square&noscroll=1
noscrolllayout=mobile + noscroll=1 for the ultimate mobile app experience:https://play.3speak.tv/embed?v=author/permlink&mode=iframe&layout=mobile&noscroll=1Control the player from outside the iframe using the postMessage API. Use &controls=0 to hide the built-in controls and provide your own UI.
// Embed with hidden controls: &controls=0
// <iframe src="...?v=author/id&mode=iframe&controls=0">
// Setup: Get iframe and create helper
const iframe = document.getElementById('player');
const send = (cmd, data = {}) =>
iframe.contentWindow.postMessage({ type: cmd, ...data }, '*');
// PLAYBACK CONTROLS
send('toggle-play'); // Toggle play/pause
send('play'); // Start playing
send('pause'); // Pause video
send('toggleFullscreen'); // Toggle fullscreen
// VOLUME CONTROLS
send('setVolume', { volume: 0.5 }); // Set to 50%
send('volumeUp', { step: 0.1 }); // +10%
send('volumeDown', { step: 0.2 }); // -20%
send('toggleMute'); // Toggle mute
send('mute'); // Mute
send('unmute'); // Unmute
// SEEKING
send('seek', { time: 30 }); // Go to 30s
send('seekForward', { seconds: 15 }); // +15s
send('seekBackward', { seconds: 5 }); // -5s
send('seekForward'); // +10s (default)
send('seekBackward'); // -10s (default)
// GET CURRENT STATE
send('getState'); // Triggers '3speak-state' event
// LISTEN FOR EVENTS
window.addEventListener('message', (e) => {
const { type, currentTime, duration,
paused, muted, volume } = e.data;
if (type === '3speak-timeupdate') {
console.log(`${currentTime}s / ${duration}s`);
}
if (type === '3speak-play') console.log('Playing');
if (type === '3speak-pause') console.log('Paused');
if (type === '3speak-ended') console.log('Ended');
});
| Command | Parameters | Description |
|---|---|---|
toggle-play |
- | Toggle play/pause |
play |
- | Start playback |
pause |
- | Pause playback |
toggleFullscreen |
- | Toggle fullscreen mode |
setVolume |
{ volume: 0-1 } |
Set volume level (0 to 1) |
volumeUp |
{ step: 0.1 } |
Increase volume by step (default 0.1) |
volumeDown |
{ step: 0.1 } |
Decrease volume by step (default 0.1) |
mute |
- | Mute audio |
unmute |
- | Unmute audio |
seek |
{ time: seconds } |
Seek to specific time in seconds |
seekForward |
{ seconds: 10 } |
Skip forward (default 10 seconds) |
seekBackward |
{ seconds: 10 } |
Skip backward (default 10 seconds) |
getState |
- | Request current player state (triggers 3speak-state event) |
The player sends these events to the parent window via postMessage:
| Event | Data | Description |
|---|---|---|
3speak-timeupdate |
currentTime, duration, paused, muted, volume |
Sent ~4x per second during playback |
3speak-durationchange |
duration |
Sent when video duration becomes available |
3speak-play |
- | Sent when playback starts |
3speak-pause |
- | Sent when playback pauses |
3speak-ended |
- | Sent when video ends |
3speak-state |
currentTime, duration, paused, muted, volume, ended |
Response to getState command |
Simple JavaScript to auto-embed 3speak URLs:
// Detect and convert 3speak URLs to iframes
const videoRegex = /https:\/\/(play\.)?3speak\.tv\/(watch|embed)\?v=([^&\s]+)/g;
const content = "Check out: https://play.3speak.tv/watch?v=meno/1czchhmr";
const embeddedContent = content.replace(videoRegex, (match, subdomain, route, videoId) => {
return `<iframe src="https://play.3speak.tv/${route}?v=${videoId}&mode=iframe"
width="854" height="480" frameborder="0" allowfullscreen></iframe>`;
});
3speak-player-ready with video dimensionsvempromundo/hkh2vzzf (vertical), meno/1czchhmr (horizontal)&mode=iframe for clean display