Prototype a Video Player in Framer

Todd Hamilton
5 min readJun 16, 2021

[Originally Posted on December 10, 2014]

One of the great things about Framer is that it supports HTML5 video. In this tutorial I’ll show you how to prototype a video player with simple controls inside Framer Studio.

👉 Download the sample file

Setting up the video.

To start, let’s get a video playing inside Framer Studio with no controls. Download a short mp4 from Vimeo ‐ I used this one.

Once you have your video, create a new fullscreen Framer Studio project, save it, and place the video file inside the images folder. Then copy paste this code into the editor:

# setup a container to hold everything
videoContainer = new Layer
width:640
height:360
backgroundColor:'#fff'
shadowBlur:2
shadowColor:'rgba(0,0,0,0.24)'

# create the video layer
videoLayer = new VideoLayer
width: 640
height: 360
video: "images/your_video.mp4"
superLayer: videoContainer

# center everything on screen
videoContainer.center()

Make sure you replace your_video.mp4 with the one you downloaded. You should now see your video centered on screen. Pretty easy so far :)

Next, let’s have our video start and stop on click. All you need for this is a simple conditional statement to check if the video is already paused or playing:

# when the video is clicked
videoLayer.on Events.Click, ->
# check if the player is paused
if videoLayer.player.paused == true
# if true call the play method on the video layer
videoLayer.player.play()
else
# else pause the video
videoLayer.player.pause()

Now you should be able to start and stop the video on click!

The player.play() and player.pause() methods are part of the HTMLMediaElement Interface. This is a common interface for working with media content in HTML5. You can view the full list of properties and methods here.

Adding basic controls.

Next, let’s add some controls. For a basic player we’re going to need:

  • a play/pause button
  • a mute button
  • a timeline and progress bar
  • a scrubber to move to a specific time

To keep things organized let’s create a container to hold our controls and position it towards the bottom of the video.

# control bar to hold buttons and timeline
controlBar = new Layer
width:videoLayer.width - 20
height:48
backgroundColor:'rgba(0,0,0,0.75)'
clip:false
borderRadius:'8px'
superLayer:videoContainer

# center the control bar
controlBar.center()

# position control bar towards the bottom of the video
controlBar.y = videoLayer.maxY - controlBar.height - 10

Once the control bar is in place we can start adding the actual controls. Let’s start with the play, pause, and volume buttons. Copy paste this code right after the last line:

# play button
playButton = new Layer
width:48
height:48
image:'images/play.png'
superLayer:controlBar

# on/off volume button
volumeButton = new Layer
width:48
height:48
image:'images/volume_on.png'
superLayer:controlBar

# position the volume button to the right of play
volumeButton.x = playButton.maxX

In the above code we’re adding two image layers as sublayers of the controlBar. For the icons I used Google’s Material Design Icon Set.

Now that we’ve added our buttons, let’s setup the click behavior. For this we’ll need two more click event handlers similar to the one we wrote earlier.

# Function to handle play/pause button
playButton.on Events.Click, ->
if videoLayer.player.paused == true
videoLayer.player.play()
playButton.image = "images/pause.png"
else
videoLayer.player.pause()
playButton.image = "images/play.png"

# simple bounce effect on click
playButton.scale = 1.15
playButton.animate
properties:
scale:1
time:0.1
curve:'spring(900,30,0)'

# Volume on/off toggle
volumeButton.on Events.Click, ->
if videoLayer.player.muted == false
videoLayer.player.muted = true
volumeButton.image = "images/volume_off.png"
else
videoLayer.player.muted = false
volumeButton.image = "images/volume_on.png"

# simple bounce effect on click
volumeButton.scale = 1.15
volumeButton.animate
properties:
scale:1
time:0.1
curve:'spring(900,30,0)'

You’ll notice that this time we’re also changing the image source for each button depending on whether the conditional is true or false.

We also need to go back in our code and make this happens in the first videoLayer click handler. The videoLayer click event should now look like this:

# when the video is clicked
videoLayer.on Events.Click, ->
# check if the player is paused
if videoLayer.player.paused == true
# if true call the play method on the video layer
videoLayer.player.play()
playButton.image = 'images/pause.png'
else
# else pause the video
videoLayer.player.pause()
playButton.image = 'images/play.png'

# simple bounce effect on click
playButton.scale = 1.15
playButton.animate
properties:
scale:1
time:0.1
curve:'spring(900,30,0)'

Adding a timeline and scrubber.

The last thing we need to do is add a timeline to our control bar. The timeline consists of three parts:

  • the timeline
  • a progress bar
  • a scrubber

The code to create these layers looks like this:

# white timeline bar
timeline = new Layer
width:494
height:10
y:volumeButton.midY - 5
x:volumeButton.maxX + 10
borderRadius:'10px'
backgroundColor:'#fff'
clip:false
superLayer: controlBar

# progress bar to indicate elapsed time
progress = new Layer
width:0
height:timeline.height
borderRadius:'10px'
backgroundColor:'#03A9F4'
superLayer: timeline

# scrubber to change current time
scrubber = new Layer
width:18
height:18
y:-4
borderRadius:'50%'
backgroundColor:'#fff'
shadowBlur:10
shadowColor:'rgba(0,0,0,0.75)'
superLayer: timeline

# make scrubber draggable
scrubber.draggable.enabled = true

# limit dragging along x-axis
scrubber.draggable.speedY = 0

# prevent scrubber from dragging outside of timeline
scrubber.draggable.constraints =
x:0
y:timeline.midY
width:timeline.width
height:-10

# Disable dragging beyond constraints
scrubber.draggable.overdrag = false

In the above code we’ve enabled dragging on our scrubber and limited movement along the x-axis by setting the speedY property to zero. We also set the scrubber constraints so dragging is confined to inside the timeline.

The final step is to hook up the progress bar and scrubber so that they update as the video plays, or when the scrubber is moved. Copy and paste the following code into the editor:

# Update the progress bar and scrubber as video plays
videoLayer.player.addEventListener "timeupdate", ->
# Calculate progress bar position
newPos = (timeline.width / videoLayer.player.duration) * videoLayer.player.currentTime

# Update progress bar and scrubber
scrubber.x = newPos
progress.width = newPos + 10

# Pause the video at start of drag
scrubber.on Events.DragStart, ->
videoLayer.player.pause()

# Update Video Layer to current frame when scrubber is moved
scrubber.on Events.DragMove, ->
progress.width = scrubber.x + 10

# When finished dragging set currentTime and play video
scrubber.on Events.DragEnd, ->
newTime = Utils.round(videoLayer.player.duration * (scrubber.x / timeline.width),0);
videoLayer.player.currentTime = newTime
videoLayer.player.play()
playButton.image = "images/pause.png"

As the timeupdate event is fired we calculate the new width of the progress bar based on the currentTime property.

The timeupdate event is fired when the currentTime property changes as part of normal playback.

The last three events handle the dragging behavior for the scrubber. At the start of the drag we pause the video. While the scrubber is being moved the DragMove event updates the progress bar width so that it stays attached to the scrubber. When the scrubber is released the DragEnd event fires and figures out the new time to start playing the video from.

Wrapping up.

If you followed all the steps you should have a basic video player working. Now you can take things further by customizing the controls or adding interactions when the video ends. To detect the end of the video all you need is:

videoLayer.player.on "ended", ->
print "video ended"

Thanks for reading!

👨‍💻 Portfolio | 🐦 Follow Me on Twitter

--

--