Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

[SugarCube 2] Integrating YouTube video through JS

edited October 2016 in Help! with 2.0
Hello there!

First post here, be nice please :D
I'm totally new at Twine, finished my first simple story a few days ago and beginning to work on a bigger project.
To be honest, I'm not familiar with javascript at all...
For information, I'm using Sugarcube 2.

So I'm trying to integration a youtube video in a passage via javascript and the youtube api (so I can control the player externally).
I'm using a widget macro to create the iframe but I have a problem. I want to be able to set the passage name as the videoId (see code). But I'm facing two different problems: either I use a <script></script> tag and the youtube api works fine but the State.variables object doesn't, or I use <<script>> macro and the youtube code doesn't give me anything...

Here the code:
<<widget "video">>
<<set $videoid to passage()>>
var tag = document.createElement('script');
tag.src = "htps://";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var videoID = State.variables.videoid;
var player;
function onYouTubeIframeAPIReady() {
	player = new YT.Player('gameplayer', {
	width: '1600',
	height: '900',
	playerVars: {
		'autoplay': 1,
		'controls': 0,
		'disablekb': 0,
		'enablejsapi': 1,
		'iv_load_policy': 3,
		'fs': 0,
		'modestbranding': 1,
		'rel': 0,
		'showinfo': 0
	videoId: videoID
(The iframe api url is not correct (htps) became the forum displays a weird thing if I leave https.)
The passage name being for example M7lc1UVf-VE
I'm very new to JS and Twine, my problem might not actually be one but I can't figure it out...


  • Story formats are web applications, not simple web pages. You should not be injecting the API's <script> element every single time you want to play a video. You have other issues as well—e.g. the width and height properties should be numbers, not strings.

    You didn't show the element which the YouTube player should replace. Are you placing it on the page somewhere?

    Also, why are you naming passages after YouTube video IDs, rather than simply passing the ID in as an argument to the widget? It seems like the latter would be far more simple.


    I'd suggest putting the API loader within your script section so it is only loaded once at startup, via the following code: (Twine 2: Story JavaScript; Twine 1: script-tagged passage)
    (function () {
    	window.onYouTubeIframeAPIReady = function () {
    			id   : 'script-user-youtube-iframe-api',
    			type : 'text/javascript',
    			src  : 'htps://'
    NOTE: As you've noticed, the forums try to load some URLs, so the one used above is intentionally incorrect. Simply change the htps: to https:.

    The above will also wrap the API load within SugarCube's load screen, so the player cannot proceed until it's ready.

    For the player widget itself:
    1. Your example widget is generating line breaks you probably do not want.
    2. I'd suggest passing the video ID into the widget as an argument. If you really want to continue using passage names, simply replace $args[0] with passage() in the following example.
    3. I'd suggest passing the video ID to the new player via the special setup object, rather than via a story variable. You very likely do not want it within the state history.
    4. You should be initializing new players via a single-use postdisplay task. You want to do this because story formats are web applications with an internal rendering pipeline, not simple web pages, and you should wait until the incoming passage is both finished rendering and has been inserted into the DOM.
    NOTE: I've placed the element to be replaced, gameplayer, at the top of the widget, since I don't know how you're handling it. If you are creating it within the passage, or elsewhere, then you may remove it from the widget.

    <<widget "youtube">>\
    <div id="gameplayer"></div>\
    <<set setup.videoId to $args[0]>>\
    postdisplay['youtube-player-setup'] = function (taskName) {
    	delete postdisplay[taskName];
    	var player = new YT.Player('gameplayer', {
    		width      : 1600,
    		height     : 900,
    		videoId    : setup.videoId,
    		playerVars : {
    			autoplay : 1,
    			controls : 0,
    			disablekb : 0,
    			enablejsapi : 1,
    			iv_load_policy : 3,
    			fs : 0,
    			modestbranding : 1,
    			rel : 0,
    			showinfo : 0
    /* <<youtube "video ID">> */
    <<youtube "M7lc1UVf-VE">>
  • Thank you very much!!
    I have to say I'm not familiar with Sugarcube tasks yet, but still learning...
    Helped me a lot, thanks again :)
Sign In or Register to comment.