Video element with changePlaybackRate buttons

Hello all,

I am trying to have buttons in my tiddler that can control the playback speed of the video. I do not want to rely on the built-in browser controls (which are visible when the attribute controls is present in the <video> element), because I want buttons that will work the same way regardless of the size of the video or the browser I am using.

Here is some basic html code that does what I want outside of TiddlyWiki, but does not work inside a tiddler.

I cannot simply paste this into a tiddler and have it work, even if I set the tiddler’s content-type to ‘text/html’

Could you please tell me what I need to do to get this code to work inside a tiddler in a tiddlywiki?

<html>
<head>
    <title>Video Playback Speed</title>
</head>
<body>
    <video id="myVideo" controls>
        <source src="file.mp4" type="video/mp4">
        Your browser does not support the video tag.
    </video>
    
    <div>
        <button onclick="changePlaybackRate(0.5)">0.5x</button>
        <button onclick="changePlaybackRate(1)">1x</button>
        <button onclick="changePlaybackRate(1.5)">1.5x</button>
        <button onclick="changePlaybackRate(2)">2x</button>
    </div>

    <script>
        function changePlaybackRate(rate) {
            const video = document.getElementById('myVideo');
            video.playbackRate = rate;
        }
    </script>
</body>
</html>

I apologize if you already know this but you can’t have JavaScript in a tiddler. There are workarounds like a plugin by tobibeer but it creates a major security issue.

Thanks for your reply! No, I did not know this.

The use case is entirely personaI, and the wiki will be run locally on my devices, so do you think it is OK to use the workarounds you mentioned?

I am an enthusiastic tiddlywiki user but I do not know how to code in Javascript (only a bit of Python) and I used AI to generate that example html code I originally posted.

If it’s not too detailed an answer, could you explain more about the simplest workaround that would work for being able to get that code working in a tiddler? (bearing in mind, for the security concerns, I will only be running the wikis locally on my personal devices).

Thank you!

I feel your discombobulation too.

The point, I think, is TW won’t load your JS unless it is loaded into the headers at start-up.

See: Raw Markup for clues.

I am not a programmer so can only hazard a guess.

You do need an experienced TW programmer to advise you fully.

Best, TT

1 Like

You might be able to use an embedded IFRAME with dynamically-loaded HTML.

Try creating a tiddler (e.g., “MyVideoPlayer”), containing:

\define html()
data:text/html,
<html>
<head>
    <title>Video Playback Speed</title>
</head>
<body>
    <video id="myVideo" controls style="width:100%;height:90vh;">
        <source src="file.mp4" type="video/mp4">
        Your browser does not support the video tag.
    </video>
    
    <div>
        <button onclick="changePlaybackRate(0.5)">0.5x</button>
        <button onclick="changePlaybackRate(1)">1x</button>
        <button onclick="changePlaybackRate(1.5)">1.5x</button>
        <button onclick="changePlaybackRate(2)">2x</button>
    </div>

    <script>
        function changePlaybackRate(rate) {
            const video = document.getElementById('myVideo');
            video.playbackRate = rate;
        }
    </script>
</body>
</html>
\end

<iframe src=<<html>> style="width:100%;height:50vh;"/>

Notes:

  • The \define html() macro is the source for the IFRAME.
    • Note how it starts with “data:text/html,”. This creates a “Data URI”, where everything that follows the first line is your desired HTML content to be rendered into the IFRAME.
    • Also note that the video element has style="width:100%;height:90vh;". This tells it to fill the entire IFRAME with some space left at the bottom for the playback rate buttons.
  • The IFRAME uses the <<html>> macro text as the src attribute.
  • The IFRAME style="…" controls the size of the IFRAME container itself. You can adjust these values to suit your preferences.

Let me know how it goes…

enjoy,
-e

2 Likes

https://tobibeer.github.io/tw/enable-js/#script

Here is the macro with instructions. I use it on some personal wikis to scrolling tickers. But as I was told many times please be very cautious because that creates serious vulnerabilities.

Keep searching as well there are some great plugins and solutions out there for media but it can be difficult to find them. There is one for video I’ll look in my wiki but I don’t know if it does speed which I find essential. I can hardly stand listening to people at normal speed!

Edit: it is a macro not a plugin

1 Like

Thank you TiddlyTitch, EricShulman, and digitalap3 for your replies!

It’s great to get a reply from you Eric, I remember your tools from years back when I first started tinkering with tiddly wiki! I must be doing something wrong. I tried your code, and I can see the frame and the buttons when I put the code into a tiddler, but the video doesn’t load for me. Is there something I missed?

I also tried to follow TiddlyTitch’s suggestion, with some success, but I have come stuck.

First I created a tiddler called videospeed with the tag $:/tags/RawMarkup and the type text/html and the following content:

    <div>
        <button onclick="changePlaybackRate(0.5)">0.5x</button>
        <button onclick="changePlaybackRate(1)">1x</button>
        <button onclick="changePlaybackRate(1.5)">1.5x</button>
        <button onclick="changePlaybackRate(2)">2x</button>
    </div>

    <script>
        function changePlaybackRate(rate) {
            const video = document.getElementById('myVideo');
            video.playbackRate = rate;
        }
    </script>

Then I created a normal tiddler to view the video called video_tiddler. I want to be able to select between different videos, so the tiddler needs to be refreshed each time I make a change which is why I have the checkbox. Here is the content of video_tiddler.

<$select tiddler=video_tiddler field=currentvideo>
<option value='file1.mp4'> video 1 </option>
<option value='file2.mp4'> video 2 </option>
</$select>

<$checkbox tiddler=video_tiddler field=showvideo checked=y unchecked=n default=n>Show video</$checkbox>

<$list filter='[{video_tiddler!!showvideo}match[y]]'>
      <video id="myVideo" controls>
        <source src={{video_tiddler!!currentvideo}} type="video/mp4">
    </video>
</$list>

Then I did a save and reload of the wiki for the changes to take effect.

The playback rate buttons now appear at the very bottom of the wiki.

The speed of playback of a video within the video_tiddler wiki does change when I press one of the buttons. And when I select a different video, and refresh the tiddler by checking and unchecking the “Show video” checkbox, the playback rate buttons are also able to change the speed of the newly selected video that is being displayed.

So, some success!

However, this is not ideal, as the buttons are always at the bottom of the page rather than in the tiddler where the video is playing.

To be able to place the buttons where I want, I tried the following:

I removed the following buttons code from videospeed ( the $:/tags/RawMarkup tagged tiddler):

    <div>
        <button onclick="changePlaybackRate(0.5)">0.5x</button>
        <button onclick="changePlaybackRate(1)">1x</button>
        <button onclick="changePlaybackRate(1.5)">1.5x</button>
        <button onclick="changePlaybackRate(2)">2x</button>
    </div>

leaving just the following in the $:/tags/RawMarkup tagged tiddler:

    <script>
        function changePlaybackRate(rate) {
            const video = document.getElementById('myVideo');
            video.playbackRate = rate;
        }
    </script>

Then I put the buttons code that I had just removed from the $:/tags/RawMarkup tagged tiddler into the video_tiddler. And then saved and reloaded the wiki.

However, although the buttons are now in the desired place, they no longer work in changing the playback speed of the current video.

If anyone has suggestions to get this working either following TiddlyTitch’s $:/tags/RawMarkup idea (as I have tried above), or following Eric’s IFRAME approach, or indeed digitalap3’s suggestion, I would be very grateful!

And I think it could also be useful in general for users who are not expert coders to know how to incorporate javascript code that seems to work fine outside of Tiddlywiki. (Because it’s the kind of thing we non-expert users will do: ask AI to make some simple html/javascript code, and then try and paste it into a tiddler).

Here’s a (tested!) solution:

First, create a tiddler (e.g., “myMacros/videospeed.js”), containing:

/*\
title: mymacros/videospeed.js
type: application/javascript
module-type: macro
\*/

/*
Macro to set video speed for myVideo element
*/

"use strict";
exports.name = "videospeed";
exports.params = [
	{name: "id"},
	{name: "rate"}
];
exports.run = function(id,rate) {
	const video = document.getElementById(id);
	video.playbackRate = rate;
	return rate;
};

IMPORTANT: make sure to set the tiddler’s type field to application/javascript and add a module-type field set to macro. Then save-and-reload for this custom javascript macro to be available.

Next, create your “video_tiddler”, containing:

<$select tiddler=video_tiddler field=currentvideo>
<option value="file1.mp4"> video 1 </option>
<option value='file2.mp4'> video 2 </option>
</$select>

<$let vid={{!!currentvideo}}>
<video id="myVideo" controls>
	<source src=<<vid>>>
	Your browser does not support the video tag.
</video>
<div>
	<$button actions="<<videospeed myVideo 0.5>>">0.5x</$button>
	<$button actions="<<videospeed myVideo 1>>">1x</$button>
	<$button actions="<<videospeed myVideo 1.5>>">1.5x</$button>
	<$button actions="<<videospeed myVideo 2>>">2x</$button>
</div>
</$let>

Notes:

  • The custom <<videospeed>> macro takes two parameters:
    • id is the <video> element’s ID. By providing this id to the macro, it let’s you target different embedded <video> elements, not just “myVideo”.
    • rate is the desired playback rate
  • The $let widget gets the currentvideo value that is stored in the tiddler.
  • By using a $let widget, a refresh of everything that is contained between <$let> and </$let> is automatically triggered whenever the vid variable value changes. Thus, choosing a video from the $select widget causes a refresh of the <video> element, and the <source> element uses this value. This eliminates the need for a $checkbox widget to trigger the refresh.
  • The $button widgets invoke the custom <<videospeed>> macro when they are pressed, which performs the javascript code that sets the video element’s playback speed.

enjoy,
-e

2 Likes

Thank you so much, Eric, it works perfectly!

Not only that, you have taught me how to make a macro, which seemed a bit too daunting and complex to learn how to do myself. And thank you for the clear and helpful notes.

You have given me enough insight and confidence to try and make other macros now, starting with play and pause buttons!