Let's make a Picture in Picture Countdown timer on the Web

Posted on January 28, 2020

Caution: This currently works only on Chrome. Firefox still does not have support for programmatic PiP.

Can I Use - Picture in Picture

The Normal Pomodoro Timer

I’m starting off with a normal countdown pomodoro timer. I will be using AlpineJS. For the uninitiated, AlpineJS is a small JavaScript library that uses Vue/Angular like syntax to bring reactivity to the DOM. If you are familiar with Vue or Angular, then you should feel right at home with the syntax.

1<main x-data="timer()" x-init="init()">
2 <div>
3 <p x-text="text"></p>
4 </div>
5 <div class="button-container">
6 <button
7 x-text='interval ? "Pause" : "Start"'
8 @click="interval ? pause() : start()"
9 >
10 Start
11 </button>
12 <button @click="stop()">Reset</button>
13 </div>
14</main>

JavaScript:

1const zeroPad = (num, places) => String(num).padStart(places, '0');
2
3const formatTime({minutes, seconds}) = () => `${zeroPad(minutes, 2)} : ${zeroPad(seconds, 2)}`;
4
5function timer() {
6 return {
7 interval: null,
8 text: '',
9 time: {
10 minutes: 25,
11 seconds: 0,
12 },
13 init() {
14 this.text = formatTime(this.time);
15 },
16 start() {
17 this.interval = setInterval(() => {
18 const time = calculateTime(this.time);
19 this.time = time;
20 if(time.minutes === 0 && time.seconds === 0) {
21 this.stop();
22 }
23 this.text = formatTime(time);
24 }, 1000);
25 },
26 stop() {
27 clearInterval(this.interval);
28 this.interval = null;
29 this.time = {
30 minutes: 25,
31 seconds: 0,
32 }
33 this.text = formatTime(this.time);
34 },
35 pause() {
36 clearInterval(this.interval);
37 this.interval = null;
38 }
39 }
40}

You need AlpineJS needed for this. You can load it from a CDN

Picture in Picture

As a sidenote, let us talk about Picture in Picture (This is where we are going at the end, but still).

Google Developers pages have some great tutorials on picture in picture.

But by essence, you can request a video to play picture in picture with:

const videoEl = document.querySelector('#video');
videoEl.requestPictureInPicture();

You can see if a device supports Picture in Picture with:

if (!('pictureInPictureEnabled' in document)) {
console.log('The Picture-in-Picture Web API is not available.');
} else if (!document.pictureInPictureEnabled) {
console.log('The Picture-in-Picture Web API is disabled.');
}

Bringing Canvas into the Picture

How do we bring the canvas into this? Well, Chrome allows for any video to play Picture in Picture and luckily for us, Canvas comes with a captureStream API that allows us to capture any stream on a canvas and capture it on video all in real time.

Here is how we would do that:

1const canvas = document.createElement('canvas');
2// Draw something to canvas.
3canvas.getContext('2d').fillRect(0, 0, canvas.width, canvas.height);
4
5const video = document.createElement('video');
6video.muted = true;
7video.srcObject = canvas.captureStream();
8video.play();

All Together now

Buy me a coffeeBuy me a coffee