diff --git a/static/videojs/videojs-markers.js b/static/videojs/videojs-markers.js
new file mode 100644
index 0000000000000000000000000000000000000000..d89b4531485cd8fedf83d0a81d389ca5eee55738
--- /dev/null
+++ b/static/videojs/videojs-markers.js
@@ -0,0 +1,379 @@
+/*! videojs-markers - v0.6.1 - 2016-10-24
+* Copyright (c) 2016 ; Licensed  */
+'use strict';
+
+(function ($, videojs, undefined) {
+  // default setting
+  var defaultSetting = {
+    markerStyle: {
+      'width': '7px',
+      'border-radius': '30%',
+      'background-color': 'red'
+    },
+    markerTip: {
+      display: true,
+      text: function text(marker) {
+        return "Break: " + marker.text;
+      },
+      time: function time(marker) {
+        return marker.time;
+      }
+    },
+    breakOverlay: {
+      display: false,
+      displayTime: 3,
+      text: function text(marker) {
+        return "Break overlay: " + marker.overlayText;
+      },
+      style: {
+        'width': '100%',
+        'height': '20%',
+        'background-color': 'rgba(0,0,0,0.7)',
+        'color': 'white',
+        'font-size': '17px'
+      }
+    },
+    onMarkerClick: function onMarkerClick(marker) {},
+    onMarkerReached: function onMarkerReached(marker, index) {},
+    markers: []
+  };
+
+  // create a non-colliding random number
+  function generateUUID() {
+    var d = new Date().getTime();
+    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+      var r = (d + Math.random() * 16) % 16 | 0;
+      d = Math.floor(d / 16);
+      return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);
+    });
+    return uuid;
+  };
+
+  var NULL_INDEX = -1;
+
+  function registerVideoJsMarkersPlugin(options) {
+    /**
+     * register the markers plugin (dependent on jquery)
+     */
+
+    var setting = $.extend(true, {}, defaultSetting, options),
+        markersMap = {},
+        markersList = [],
+        // list of markers sorted by time
+    videoWrapper = $(this.el()),
+        currentMarkerIndex = NULL_INDEX,
+        player = this,
+        markerTip = null,
+        breakOverlay = null,
+        overlayIndex = NULL_INDEX;
+
+    function sortMarkersList() {
+      // sort the list by time in asc order
+      markersList.sort(function (a, b) {
+        return setting.markerTip.time(a) - setting.markerTip.time(b);
+      });
+    }
+
+    function addMarkers(newMarkers) {
+      newMarkers.forEach(function (marker) {
+        marker.key = generateUUID();
+
+        videoWrapper.find('.vjs-progress-holder').append(createMarkerDiv(marker));
+
+        // store marker in an internal hash map
+        markersMap[marker.key] = marker;
+        markersList.push(marker);
+      });
+
+      sortMarkersList();
+    }
+
+    function getPosition(marker) {
+      return setting.markerTip.time(marker) / player.duration() * 100;
+    }
+
+    function createMarkerDiv(marker) {
+      var markerDiv = $("<div class='vjs-marker'></div>");
+      markerDiv.css(setting.markerStyle).css({
+        "margin-left": -parseFloat(markerDiv.css("width")) / 2 + 'px',
+        "left": getPosition(marker) + '%'
+      }).attr("data-marker-key", marker.key).attr("data-marker-time", setting.markerTip.time(marker));
+
+      // add user-defined class to marker
+      if (marker.class) {
+        markerDiv.addClass(marker.class);
+      }
+
+      // bind click event to seek to marker time
+      markerDiv.on('click', function (e) {
+        var preventDefault = false;
+        if (typeof setting.onMarkerClick === "function") {
+          // if return false, prevent default behavior
+          preventDefault = setting.onMarkerClick(marker) === false;
+        }
+
+        if (!preventDefault) {
+          var key = $(this).data('marker-key');
+          player.currentTime(setting.markerTip.time(markersMap[key]));
+        }
+      });
+
+      if (setting.markerTip.display) {
+        registerMarkerTipHandler(markerDiv);
+      }
+
+      return markerDiv;
+    }
+
+    function updateMarkers() {
+      // update UI for markers whose time changed
+      markersList.forEach(function (marker) {
+        var markerDiv = videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key + "']");
+        var markerTime = setting.markerTip.time(marker);
+
+        if (markerDiv.data('marker-time') !== markerTime) {
+          markerDiv.css({ "left": getPosition(marker) + '%' }).attr("data-marker-time", markerTime);
+        }
+      });
+      sortMarkersList();
+    }
+
+    function removeMarkers(indexArray) {
+      // reset overlay
+      if (!!breakOverlay) {
+        overlayIndex = NULL_INDEX;
+        breakOverlay.css("visibility", "hidden");
+      }
+      currentMarkerIndex = NULL_INDEX;
+
+      var deleteIndexList = [];
+      indexArray.forEach(function (index) {
+        var marker = markersList[index];
+        if (marker) {
+          // delete from memory
+          delete markersMap[marker.key];
+          deleteIndexList.push(index);
+
+          // delete from dom
+          videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key + "']").remove();
+        }
+      });
+
+      // clean up markers array
+      deleteIndexList.reverse();
+      deleteIndexList.forEach(function (deleteIndex) {
+        markersList.splice(deleteIndex, 1);
+      });
+
+      // sort again
+      sortMarkersList();
+    }
+
+    // attach hover event handler
+    function registerMarkerTipHandler(markerDiv) {
+      markerDiv.on('mouseover', function () {
+        var marker = markersMap[$(markerDiv).data('marker-key')];
+
+        if (!!markerTip) {
+          markerTip.find('.vjs-tip-inner').text(setting.markerTip.text(marker));
+
+          // margin-left needs to minus the padding length to align correctly with the marker
+          markerTip.css({
+            "left": getPosition(marker) + '%',
+            "margin-left": -parseFloat(markerTip.width()) / 2 - 5 + 'px',
+            "visibility": "visible"
+          });
+        }
+      });
+
+      markerDiv.on('mouseout', function () {
+        !!markerTip && markerTip.css("visibility", "hidden");
+      });
+    }
+
+    function initializeMarkerTip() {
+      markerTip = $("<div class='vjs-tip'><div class='vjs-tip-arrow'></div><div class='vjs-tip-inner'></div></div>");
+      videoWrapper.find('.vjs-progress-holder').append(markerTip);
+    }
+
+    // show or hide break overlays
+    function updateBreakOverlay() {
+      if (!setting.breakOverlay.display || currentMarkerIndex < 0) {
+        return;
+      }
+
+      var currentTime = player.currentTime();
+      var marker = markersList[currentMarkerIndex];
+      var markerTime = setting.markerTip.time(marker);
+
+      if (currentTime >= markerTime && currentTime <= markerTime + setting.breakOverlay.displayTime) {
+        if (overlayIndex !== currentMarkerIndex) {
+          overlayIndex = currentMarkerIndex;
+          breakOverlay && breakOverlay.find('.vjs-break-overlay-text').html(setting.breakOverlay.text(marker));
+        }
+
+        breakOverlay && breakOverlay.css('visibility', "visible");
+      } else {
+        overlayIndex = NULL_INDEX;
+        breakOverlay && breakOverlay.css("visibility", "hidden");
+      }
+    }
+
+    // problem when the next marker is within the overlay display time from the previous marker
+    function initializeOverlay() {
+      breakOverlay = $("<div class='vjs-break-overlay'><div class='vjs-break-overlay-text'></div></div>").css(setting.breakOverlay.style);
+      videoWrapper.append(breakOverlay);
+      overlayIndex = NULL_INDEX;
+    }
+
+    function onTimeUpdate() {
+      onUpdateMarker();
+      updateBreakOverlay();
+      options.onTimeUpdateAfterMarkerUpdate && options.onTimeUpdateAfterMarkerUpdate();
+    }
+
+    function onUpdateMarker() {
+      /*
+        check marker reached in between markers
+        the logic here is that it triggers a new marker reached event only if the player
+        enters a new marker range (e.g. from marker 1 to marker 2). Thus, if player is on marker 1 and user clicked on marker 1 again, no new reached event is triggered)
+      */
+      if (!markersList.length) {
+        return;
+      }
+
+      var getNextMarkerTime = function getNextMarkerTime(index) {
+        if (index < markersList.length - 1) {
+          return setting.markerTip.time(markersList[index + 1]);
+        }
+        // next marker time of last marker would be end of video time
+        return player.duration();
+      };
+      var currentTime = player.currentTime();
+      var newMarkerIndex = NULL_INDEX;
+
+      if (currentMarkerIndex !== NULL_INDEX) {
+        // check if staying at same marker
+        var nextMarkerTime = getNextMarkerTime(currentMarkerIndex);
+        if (currentTime >= setting.markerTip.time(markersList[currentMarkerIndex]) && currentTime < nextMarkerTime) {
+          return;
+        }
+
+        // check for ending (at the end current time equals player duration)
+        if (currentMarkerIndex === markersList.length - 1 && currentTime === player.duration()) {
+          return;
+        }
+      }
+
+      // check first marker, no marker is selected
+      if (currentTime < setting.markerTip.time(markersList[0])) {
+        newMarkerIndex = NULL_INDEX;
+      } else {
+        // look for new index
+        for (var i = 0; i < markersList.length; i++) {
+          nextMarkerTime = getNextMarkerTime(i);
+          if (currentTime >= setting.markerTip.time(markersList[i]) && currentTime < nextMarkerTime) {
+            newMarkerIndex = i;
+            break;
+          }
+        }
+      }
+
+      // set new marker index
+      if (newMarkerIndex !== currentMarkerIndex) {
+        // trigger event if index is not null
+        if (newMarkerIndex !== NULL_INDEX && options.onMarkerReached) {
+          options.onMarkerReached(markersList[newMarkerIndex], newMarkerIndex);
+        }
+        currentMarkerIndex = newMarkerIndex;
+      }
+    }
+
+    // setup the whole thing
+    function initialize() {
+      if (setting.markerTip.display) {
+        initializeMarkerTip();
+      }
+
+      // remove existing markers if already initialized
+      player.markers.removeAll();
+      addMarkers(options.markers);
+
+      if (setting.breakOverlay.display) {
+        initializeOverlay();
+      }
+      onTimeUpdate();
+      player.on("timeupdate", onTimeUpdate);
+    }
+
+    // setup the plugin after we loaded video's meta data
+    player.on("loadedmetadata", function () {
+      initialize();
+    });
+
+    // exposed plugin API
+    player.markers = {
+      getMarkers: function getMarkers() {
+        return markersList;
+      },
+      next: function next() {
+        // go to the next marker from current timestamp
+        var currentTime = player.currentTime();
+        for (var i = 0; i < markersList.length; i++) {
+          var markerTime = setting.markerTip.time(markersList[i]);
+          if (markerTime > currentTime) {
+            player.currentTime(markerTime);
+            break;
+          }
+        }
+      },
+      prev: function prev() {
+        // go to previous marker
+        var currentTime = player.currentTime();
+        for (var i = markersList.length - 1; i >= 0; i--) {
+          var markerTime = setting.markerTip.time(markersList[i]);
+          // add a threshold
+          if (markerTime + 0.5 < currentTime) {
+            player.currentTime(markerTime);
+            return;
+          }
+        }
+      },
+      add: function add(newMarkers) {
+        // add new markers given an array of index
+        addMarkers(newMarkers);
+      },
+      remove: function remove(indexArray) {
+        // remove markers given an array of index
+        removeMarkers(indexArray);
+      },
+      removeAll: function removeAll() {
+        var indexArray = [];
+        for (var i = 0; i < markersList.length; i++) {
+          indexArray.push(i);
+        }
+        removeMarkers(indexArray);
+      },
+      updateTime: function updateTime() {
+        // notify the plugin to update the UI for changes in marker times
+        updateMarkers();
+      },
+      reset: function reset(newMarkers) {
+        // remove all the existing markers and add new ones
+        player.markers.removeAll();
+        addMarkers(newMarkers);
+      },
+      destroy: function destroy() {
+        // unregister the plugins and clean up even handlers
+        player.markers.removeAll();
+        breakOverlay && breakOverlay.remove();
+        markerTip && markerTip.remove();
+        player.off("timeupdate", updateBreakOverlay);
+        delete player.markers;
+      }
+    };
+  }
+
+  videojs.plugin('markers', registerVideoJsMarkersPlugin);
+})(jQuery, window.videojs);
+//# sourceMappingURL=videojs-markers.js.map
diff --git a/static/videojs/videojs.markers.css b/static/videojs/videojs.markers.css
new file mode 100644
index 0000000000000000000000000000000000000000..7655a64f877f1876e46c464435aedb29b5bd99b8
--- /dev/null
+++ b/static/videojs/videojs.markers.css
@@ -0,0 +1,59 @@
+.vjs-marker {
+  position: absolute;
+  left: 0;
+  bottom: 0em;
+  opacity: 1;
+  height: 100%;
+  transition: opacity .2s ease;
+  -webkit-transition: opacity .2s ease;
+  -moz-transition: opacity .2s ease;
+  z-index: 100;
+}
+.vjs-marker:hover {
+  cursor: pointer;
+  -webkit-transform: scale(1.3, 1.3);
+  -moz-transform: scale(1.3, 1.3);
+  -o-transform: scale(1.3, 1.3);
+  -ms-transform: scale(1.3, 1.3);
+  transform: scale(1.3, 1.3);
+}
+.vjs-tip {
+  visibility: hidden;
+  display: block;
+  opacity: 0.8;
+  padding: 5px;
+  font-size: 10px;
+  position: absolute;
+  bottom: 14px;
+  z-index: 100000;
+}
+.vjs-tip .vjs-tip-arrow {
+  background: url(data:image/gif;base64,R0lGODlhCQAJAIABAAAAAAAAACH5BAEAAAEALAAAAAAJAAkAAAIRjAOnwIrcDJxvwkplPtchVQAAOw==) no-repeat top left;
+  bottom: 0;
+  left: 50%;
+  margin-left: -4px;
+  background-position: bottom left;
+  position: absolute;
+  width: 9px;
+  height: 5px;
+}
+.vjs-tip .vjs-tip-inner {
+  border-radius: 3px;
+  -moz-border-radius: 3px;
+  -webkit-border-radius: 3px;
+  padding: 5px 8px 4px 8px;
+  background-color: black;
+  color: white;
+  max-width: 200px;
+  text-align: center;
+}
+.vjs-break-overlay {
+  visibility: hidden;
+  position: absolute;
+  z-index: 100000;
+  top: 0;
+}
+.vjs-break-overlay .vjs-break-overlay-text {
+  padding: 9px;
+  text-align: center;
+}