<body>
  <svg display="none" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><symbol id="icon-downlaod" viewBox="0 0 745 1024"><title>downlaod</title><path class="path1" d="M682.545 791.273h-620.364q-6.545 0-11.091-4.545t-4.545-11.091v-93.091q0-6.182 4.545-10.727t11.091-4.545h620.364q6.545 0 11.091 4.545t4.545 10.727v93.091q0 6.545-4.545 11.091t-11.091 4.545zM693.818 290.545l-310.182 310.182q-6.182 4.364-11.273 4.364t-11.273-4.364l-310.182-310.182q-6.182-6.182-3.636-17.455 4-9.455 14.909-9.455h170.545v-201.455q0-6.545 4.545-11.091t11.091-4.545h248q6.545 0 11.091 4.545t4.545 11.091v201.455h170.545q10.909 0 14.909 9.455 2.545 11.273-3.636 17.455z"></path></symbol></defs></svg>

  <div class="page-wrap">
    <header class="site-header wrapper">
      <h1><span class="script">Petersen Family</span>Christmas<br>Songbook</h1>
    </header>
    <section role="main" class="main wrapper">
      <a href="https://soundcloud.com/nicholas_petersen/sets/petersen-family-christmas" class="sc-player">Listen</a>
      <a href="http://nicholaspetersen.is/assets/songbook/PetersenFamilyChristmasSongbook.zip" class="button"><svg class="icon-downlaod"><use xlink:href="#icon-downlaod"></use></svg> Download</a>
    </section>
    <footer>
      <p class="small">Copyright ©2014 Nicholas Petersen</p>
      <a href="https://codepen.io" class="codepen-logo" style="display:none;">
      <svg style="display: none;" xmlns="http://www.w3.org/2000/svg"><symbol id="codepen-logo" viewBox="0 0 120 120"><path class="outer-ring" d="M60.048 0C26.884 0 0 26.9 0 60.048s26.884 60 60 60.047c33.163 0 60.047-26.883 60.047-60.047 S93.211 0 60 0z M60.048 110.233c-27.673 0-50.186-22.514-50.186-50.186S32.375 9.9 60 9.9 c27.672 0 50.2 22.5 50.2 50.186S87.72 110.2 60 110.233z"/><path class="inner-box" d="M97.147 48.319c-0.007-0.047-0.019-0.092-0.026-0.139c-0.016-0.09-0.032-0.18-0.056-0.268 c-0.014-0.053-0.033-0.104-0.05-0.154c-0.025-0.078-0.051-0.156-0.082-0.232c-0.021-0.053-0.047-0.105-0.071-0.156 c-0.033-0.072-0.068-0.143-0.108-0.211c-0.029-0.051-0.061-0.1-0.091-0.148c-0.043-0.066-0.087-0.131-0.135-0.193 c-0.035-0.047-0.072-0.094-0.109-0.139c-0.051-0.059-0.104-0.117-0.159-0.172c-0.042-0.043-0.083-0.086-0.127-0.125 c-0.059-0.053-0.119-0.104-0.181-0.152c-0.048-0.037-0.095-0.074-0.145-0.109c-0.019-0.012-0.035-0.027-0.053-0.039L61.817 23.5 c-1.072-0.715-2.468-0.715-3.54 0L24.34 46.081c-0.018 0.012-0.034 0.027-0.053 0.039c-0.05 0.035-0.097 0.072-0.144 0.1 c-0.062 0.049-0.123 0.1-0.181 0.152c-0.045 0.039-0.086 0.082-0.128 0.125c-0.056 0.055-0.108 0.113-0.158 0.2 c-0.038 0.045-0.075 0.092-0.11 0.139c-0.047 0.062-0.092 0.127-0.134 0.193c-0.032 0.049-0.062 0.098-0.092 0.1 c-0.039 0.068-0.074 0.139-0.108 0.211c-0.024 0.051-0.05 0.104-0.071 0.156c-0.031 0.076-0.057 0.154-0.082 0.2 c-0.017 0.051-0.035 0.102-0.05 0.154c-0.023 0.088-0.039 0.178-0.056 0.268c-0.008 0.047-0.02 0.092-0.025 0.1 c-0.019 0.137-0.029 0.275-0.029 0.416V71.36c0 0.1 0 0.3 0 0.418c0.006 0 0 0.1 0 0.1 c0.017 0.1 0 0.2 0.1 0.268c0.015 0.1 0 0.1 0.1 0.154c0.025 0.1 0.1 0.2 0.1 0.2 c0.021 0.1 0 0.1 0.1 0.154c0.034 0.1 0.1 0.1 0.1 0.213c0.029 0 0.1 0.1 0.1 0.1 c0.042 0.1 0.1 0.1 0.1 0.193c0.035 0 0.1 0.1 0.1 0.139c0.05 0.1 0.1 0.1 0.2 0.2 c0.042 0 0.1 0.1 0.1 0.125c0.058 0.1 0.1 0.1 0.2 0.152c0.047 0 0.1 0.1 0.1 0.1 c0.019 0 0 0 0.1 0.039L58.277 96.64c0.536 0.4 1.2 0.5 1.8 0.537c0.616 0 1.233-0.18 1.77-0.537 l33.938-22.625c0.018-0.012 0.034-0.027 0.053-0.039c0.05-0.035 0.097-0.072 0.145-0.109c0.062-0.049 0.122-0.1 0.181-0.152 c0.044-0.039 0.085-0.082 0.127-0.125c0.056-0.055 0.108-0.113 0.159-0.172c0.037-0.045 0.074-0.09 0.109-0.139 c0.048-0.062 0.092-0.127 0.135-0.193c0.03-0.049 0.062-0.098 0.091-0.146c0.04-0.07 0.075-0.141 0.108-0.213 c0.024-0.051 0.05-0.102 0.071-0.154c0.031-0.078 0.057-0.156 0.082-0.234c0.017-0.051 0.036-0.102 0.05-0.154 c0.023-0.088 0.04-0.178 0.056-0.268c0.008-0.045 0.02-0.092 0.026-0.137c0.018-0.139 0.028-0.277 0.028-0.418V48.735 C97.176 48.6 97.2 48.5 97.1 48.319z M63.238 32.073l25.001 16.666L77.072 56.21l-13.834-9.254V32.073z M56.856 32.1 v14.883L43.023 56.21l-11.168-7.471L56.856 32.073z M29.301 54.708l7.983 5.34l-7.983 5.34V54.708z M56.856 88.022L31.855 71.4 l11.168-7.469l13.833 9.252V88.022z M60.048 67.597l-11.286-7.549l11.286-7.549l11.285 7.549L60.048 67.597z M63.238 88.022V73.14 l13.834-9.252l11.167 7.469L63.238 88.022z M90.794 65.388l-7.982-5.34l7.982-5.34V65.388z"/></symbol></svg>
      <svg class="logo">
        <use xlink:href="#codepen-logo"></use>
      </svg>
      </a>
    </footer>
  </div>
</body>
/* LAYOUT *
-----------------------------------------------*/
* {
	box-sizing: border-box;
}

.cf:before,
.cf:after {
  content: " "; /* 1 */
  display: table; /* 2 */
}

.cf:after {
  clear: both;
}

.cf {
  *zoom: 1;
}

@small: ~"screen and (min-width: 20em)";
@medium: ~"screen and (min-width: 38em)";
@large: ~"screen and (min-width: 48em)";
@extra-large: ~"screen and (min-width: 58em)";
@max: ~"screen and (min-width: 68em)";

/* COLORS *
-----------------------------------------------*/
@white: #f8f8f8;
@red: #dd4444;
@red-dark: #bf3d3f;
@red-darker: #9c3538;
@black: #1f1f1f;

/* FONTS *
-----------------------------------------------*/
@import url(https://fonts.googleapis.com/css?family=Sacramento|Montserrat);

.script {
	font-family: 'Sacramento', cursive;
}

.display {
	font-family: 'Montserrat', sans-serif;
}

/* GLOBAL *
-----------------------------------------------*/

body {
	margin: 0;
	background-color: @red;
	background-image: url('http://nicholaspetersen.is/wp-content/themes/v3.1/images/blackbg1-transparent.png');
	color: white;
	text-align: center;
	.display;

	@media @medium {
		font-size: 112.5%;
	}
}

.page-wrap {
	width: 100%;
	overflow: hidden;
	background: url('http://nicholaspetersen.is/wp-content/themes/v3/images/christmas-trees-mobile.png') no-repeat;
	background-position: top center;
	background-size: 100% auto;
	
	@media @medium {
		background: url('http://nicholaspetersen.is/wp-content/themes/v3/images/christmas-trees.png') no-repeat;
		background-size: 100% auto;
	}
}

.wrapper {
	width: 90%;
	max-width: 1200px;
	margin: 0 auto;
	overflow: hidden;
}

p.small {
	font-size: 0.8em;
}

a {
	color: @red-darker;

	&:hover {
		color: @red-dark
	}
}

a.sc-player,
.button {
	display: inline-block;
	background: @red-dark;
	background: rgba(19, 19, 19, 0.14);
	border-radius: 4px;
	padding: 1em 1.5em;
	text-align: center;
	text-decoration: none;
	color: @white;
	font-size: 1.5em;
	.transition;

	&:hover {
		background: @red-darker;
		background: rgba(19, 19, 19, 0.24);
		color: @white;
	}
}

.button svg {
	fill: @white;
	width: 30px;
	height: 30px;
	display: inline-block;
	vertical-align: middle;
}

.main {
	margin-bottom: 4em;
}

.transition {
	transition: all 0.3s ease-out;
}

h1 {
	position: relative;
  margin-top: 50px;
	.display;
	text-transform: uppercase;
	font-size: 2em;
	line-height: 1;
	text-shadow: 0.15em 0.1em 3px rgba(19, 19, 19, 0.22);
	-webkit-font-smoothing: antialiased;
	.transition;
	cursor: pointer;


	@media @small {
		font-size: 2.5em;
	}
	
	@media @medium {
		font-size: 4.5em;
		margin-top: 100px;
	}
	
	@media @max {
		font-size: 6em;
	}
	
	.small {
		text-transform: none;
		display: block;
		.script;
		font-weight: normal;
		font-size: 0.6em;
		position: absolute;
		left: 50%;
		margin-left: -1.75em;
		top: -0.25em;
	}
 
	.script {
		position: relative;
		z-index: 1;
    display: block;
		font-size: 1.15em;
		margin-bottom: -0.15em;
		font-weight: normal;
		text-transform: none;
	}
	
	&:hover {
		@media @medium {
			transform: translate(-0.1em, -0.1em) scale(1.05);
			text-shadow: 0.25em 0.2em 10px rgba(19, 19, 19, 0.22);
		}	
	}
}


/* FOOTER *
-----------------------------------------------*/
footer {
	background: @red-dark;
	background: rgba(19,19,19,0.14);  
	padding: 4em;
	color: @red-darker;
}

footer p {
	text-align: center;
}

.codepen-logo {
	display: none;
	max-width: 40px;
	display: block;
	margin: 0 auto;
	
	svg {
		width: 40px;
		height: 40px;
		display: block;
		fill: white;
	}
}

/* SOUNDCLOUD PLAYER *
-----------------------------------------------*/
.sc-player {
	position: relative;
	margin-bottom: 2em;
	.cf;

	a {
		text-decoration: none;
		color: #fff;
	}

	ol,
	li {
		margin: 0;
		padding: 0;
		list-style-position: inside;
	}
}

/* Artworks */
.sc-player .sc-artwork-list{
	display: none;
	float: left;
	width: 40%;
	margin-bottom: 3%;
	background-color: transparent;
	list-style-type: none;
	position: relative;
	height: 100%;

	li {
		list-style-type: none;
		display: none;
	}
	
	li.active{
		list-style-type: none;
		display: block;
	}
}

.sc-player .sc-artwork-list li img, 
.sc-player .sc-artwork-list li div {
	list-style-type: none;
	width: 100%;
	height: auto;
}

/* controls */
.sc-player .sc-controls{
	display: block;
}

.sc-player .sc-controls a {
	text-indent: -9999px;
	content: '';
	display: block;
	background: @red-dark;
	background: rgba(19, 19, 19, 0.12);
	border-radius: 50%;
	width: 150px;
	height: 150px;
	margin: 0 auto;
	position: relative;
	.transition;
	
	@media @medium {
		width: 220px;
		height: 220px;
	}
	
	&:hover {
		background: @red-darker;
		background: rgba(19, 19, 19, 0.22);
	}
	
	&:after {
		content: '';
		display: block;
		position: absolute;
		top: 50%;
		margin-top: -50px;
		left: 50%;
		margin-left: -50px;
		z-index: 1;
		//background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/35376/play-pause.png');
		background: url('http://nicholaspetersen.is/wp-content/themes/v3/images/play.png');
		background-position: top;
		background-size: 100px auto;
		border-radius: 50%;
		width: 100px;
		height: 100px;
		
		@media @medium {
			width: 180px;
			height: 180px;
			margin-top: -90px;
			margin-left: -90px;
			background-size: 180px auto;
		}
	}
}

.sc-player .sc-controls a.sc-pause:after {
	background-position: bottom;
}

.sc-scrubber .sc-time-indicators{
	background: @red-darker;
	background: rgba(19, 19, 19, 0.22);
	color: #fff;
	border-radius: 4px;
	padding: 7px;
	text-align: right;
}

.sc-player .sc-controls a.sc-pause {
	display: none;
}

.sc-player.playing .sc-controls a.sc-play {
	display: none;
}

.sc-player.playing .sc-controls a.sc-pause {
	display: block;
}

/* scrubber */

.sc-scrubber {
	position: relative;
	float: right;
	width: 100%;
	margin: 0.5em 0;
	border-radius: 4px;

	@media @medium {
		//margin-top: 20px;
	}

	.sc-time-span {
		height: 100px;
		position: relative;
	}

	.sc-buffer, 
	.sc-played {
		height: 100px;
		position: absolute;
		top: 0;
	}

	.sc-time-indicators{
		position: absolute;
		right: 0;
		top: -48px;
	}

	.sc-time-span {
		//background-color: #666;
		border-radius: 4px;
		overflow: hidden;
	}

	.sc-volume-slider {
		background-color: @red-dark;
		background-color: rgba(19,19,19,0.12);
		border-radius: 2px;
	}

	.sc-volume-slider .sc-volume-status{
		background-color: @red-darker;
		background-color: rgba(19,19,19,0.22);
		border-right: 1px solid @red-darker;
		border-right: 1px solid rgba(19,19,19,0.22);
		//border-right: 1px solid white;
	}

	.sc-waveform-container {
		z-index: 800;
		width: 100%;
		position: absolute;
	}

	.sc-time-span img {
		height: 100px;
		width: 100%;
		border-radius: 4px;
	}

	.sc-buffer {
		background: @red-dark;
		background: rgba(19, 19, 19, 0.12);
		z-index: 1;
		position: absolute;
	}

	.sc-played {
		background: @red-darker;
		background: rgba(19, 19, 19, 0.22);
		z-index: 799;
	}
}

/* volume control */
.sc-volume-slider {
	top: -35px;
	left: 0px;
	position: absolute;
	width: 150px;
	height: 20px;
	background-color: white;

	.sc-volume-status{
		position: absolute;
		width: 0%;
		height: 20px;
		top: 0;
		left: 0;
	}
}

/* tracks */

/* Track listings*/
.sc-player ol.sc-trackslist {
	position: relative;
	width: 100%;
	overflow: auto;

	li {
		width: 100%;
		cursor: pointer;
		margin-bottom: 0.5em;
		padding: 4%;
		background: @red-dark;
		background: rgba(19, 19, 19, 0.12);
		color: white;
		border-radius: 4px;
		transition: background 0.3s ease-in;
		text-align: left;

		@media @medium {
			padding: 2%;
		}

		&:last-child {
			margin-bottom: 0;
		}

		a {
			font-size: 1.125em;
			
			@media @small {

			}

			@media @medium {
				font-size: 2em;
			}
		}

		&:hover {  
			background: @red-darker;
			background: rgba(19, 19, 19, 0.22);
		}

		&.active{  
			background: @red-darker;
			background: rgba(19, 19, 19, 0.32);
			-moz-border-radius: 4px;
			-webkit-border-radius: 4px;

			 a {
				color: #fff;
			}
		}
	}
}

.sc-track-duration {
	float: right;
	margin-top: 0.25em;

	@media @medium {
		margin-top: 0.5em;
	}
}

/* Track info*/

.sc-player .sc-info {
	position: relative;
	margin-bottom: 2em;
	padding: 1% 3%;
	
	@media @medium {
		margin-top: 1em;
		margin-bottom: 3em;
	}

	h3 {
		font-size: 2em;
		margin-bottom: 0.5em;
	}
	
	h4 {
		display: none;
	}
 
	.sc-info a {
		color: #fff;
	}
}

p {
	max-width: 38em;
	max-width: 38rem;
	margin: 0 auto 1em;
	line-height: 1.5;
	text-align: left;
}

.sc-player .sc-info-toggle,
.sc-player .sc-info-close {
	display: none;
}

/* utilities */

.sc-player .hidden {
	//display: none;
}

.sc-player-engine-container{
	width: 1px;
	height: 1px;
	position: fixed;
	top: 2px;
	left: 2px;
}

.sc-player .sc-info-toggle{
	background: #22B573;
	color: #fff;
	border-radius: 4px;
	padding: 4px;
}

.sc-player .sc-info-toggle:hover{
	background: #333;
	color: #fff;
}

.sc-player .sc-info-close{
	background: #22B573;
	border-radius: 4px;
	padding: 2px 4px;
	font-weight: bold;
}
View Compiled
/*
*   SoundCloud Custom Player jQuery Plugin
*   Author: Matas Petrikas, matas@soundcloud.com
*   Copyright (c) 2009  SoundCloud Ltd.
*   Licensed under the MIT license:
*   https://www.opensource.org/licenses/mit-license.php
*
*   Usage:
*   <a href="https://soundcloud.com/matas/hobnotropic" class="sc-player">My new dub track</a>
*   The link will be automatically replaced by the HTML based player
*/
(function($) {
  // Convert milliseconds into Hours (h), Minutes (m), and Seconds (s)
  var timecode = function(ms) {
    var hms = function(ms) {
          return {
            h: Math.floor(ms/(60*60*1000)),
            m: Math.floor((ms/60000) % 60),
            s: Math.floor((ms/1000) % 60)
          };
        }(ms),
        tc = []; // Timecode array to be joined with '.'

    if (hms.h > 0) {
      tc.push(hms.h);
    }

    tc.push((hms.m < 10 && hms.h > 0 ? "0" + hms.m : hms.m));
    tc.push((hms.s < 10  ? "0" + hms.s : hms.s));

    return tc.join('.');
  };
  // shuffle the array
  var shuffle = function(arr) {
    arr.sort(function() { return 1 - Math.floor(Math.random() * 3); } );
    return arr;
  };

  var debug = true,
      useSandBox = false,
      $doc = $(document),
      log = function(args) {
        try {
          if(debug && window.console && window.console.log){
            window.console.log.apply(window.console, arguments);
          }
        } catch (e) {
          // no console available
        }
      },
      domain = useSandBox ? 'sandbox-soundcloud.com' : 'soundcloud.com',
      secureDocument = (document.location.protocol === 'https:'),
      // convert a SoundCloud resource URL to an API URL
      scApiUrl = function(url, apiKey) {
        var resolver = ( secureDocument || (/^https/i).test(url) ? 'https' : 'http') + '://api.' + domain + '/resolve?url=',
            params = 'format=json&consumer_key=' + apiKey +'&callback=?';

        // force the secure url in the secure environment
        if( secureDocument ) {
          url = url.replace(/^http:/, 'https:');
        }

        // check if it's already a resolved api url
        if ( (/api\./).test(url) ) {
          return url + '?' + params;
        } else {
          return resolver + url + '&' + params;
        }
      };

  // TODO Expose the audio engine, so it can be unit-tested
  var audioEngine = function() {
    var html5AudioAvailable = function() {
        var state = false;
        try{
          var a = new Audio();
          state = a.canPlayType && (/maybe|probably/).test(a.canPlayType('audio/mpeg'));
          // uncomment the following line, if you want to enable the html5 audio only on mobile devices
          // state = state && (/iPad|iphone|mobile|pre\//i).test(navigator.userAgent);
        }catch(e){
          // there's no audio support here sadly
        }

        return state;
    }(),
    callbacks = {
      onReady: function() {
        $doc.trigger('scPlayer:onAudioReady');
      },
      onPlay: function() {
        $doc.trigger('scPlayer:onMediaPlay');
      },
      onPause: function() {
        $doc.trigger('scPlayer:onMediaPause');
      },
      onEnd: function() {
        $doc.trigger('scPlayer:onMediaEnd');
      },
      onBuffer: function(percent) {
        $doc.trigger({type: 'scPlayer:onMediaBuffering', percent: percent});
      }
    };

    var html5Driver = function() {
      var player = new Audio(),
          onTimeUpdate = function(event){
            var obj = event.target,
                buffer = ((obj.buffered.length && obj.buffered.end(0)) / obj.duration) * 100;
            // ipad has no progress events implemented yet
            callbacks.onBuffer(buffer);
            // anounce if it's finished for the clients without 'ended' events implementation
            if (obj.currentTime === obj.duration) { callbacks.onEnd(); }
          },
          onProgress = function(event) {
            var obj = event.target,
                buffer = ((obj.buffered.length && obj.buffered.end(0)) / obj.duration) * 100;
            callbacks.onBuffer(buffer);
          };

      $('<div class="sc-player-engine-container"></div>').appendTo(document.body).append(player);

      // prepare the listeners
      player.addEventListener('play', callbacks.onPlay, false);
      player.addEventListener('pause', callbacks.onPause, false);
      // handled in the onTimeUpdate for now untill all the browsers support 'ended' event
      // player.addEventListener('ended', callbacks.onEnd, false);
      player.addEventListener('timeupdate', onTimeUpdate, false);
      player.addEventListener('progress', onProgress, false);


      return {
        load: function(track, apiKey) {
          player.pause();
          player.src = track.stream_url + (/\?/.test(track.stream_url) ? '&' : '?') + 'consumer_key=' + apiKey;
          player.load();
          player.play();
        },
        play: function() {
          player.play();
        },
        pause: function() {
          player.pause();
        },
        stop: function(){
          if (player.currentTime) {
            player.currentTime = 0;
            player.pause();
          }
        },
        seek: function(relative){
          player.currentTime = player.duration * relative;
          player.play();
        },
        getDuration: function() {
          return player.duration * 1000;
        },
        getPosition: function() {
          return player.currentTime * 1000;
        },
        setVolume: function(val) {
          player.volume = val / 100;
        }
      };

    };



    var flashDriver = function() {
      var engineId = 'scPlayerEngine',
          player,
          flashHtml = function(url) {
            var swf = (secureDocument ? 'https' : 'http') + '://player.' + domain +'/player.swf?url=' + url +'&amp;enable_api=true&amp;player_type=engine&amp;object_id=' + engineId;
            if ($.browser.msie) {
              return '<object height="100%" width="100%" id="' + engineId + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" data="' + swf + '">'+
                '<param name="movie" value="' + swf + '" />'+
                '<param name="allowscriptaccess" value="always" />'+
                '</object>';
            } else {
              return '<object height="100%" width="100%" id="' + engineId + '">'+
                '<embed allowscriptaccess="always" height="100%" width="100%" src="' + swf + '" type="application/x-shockwave-flash" name="' + engineId + '" />'+
                '</object>';
            }
          };


      // listen to audio engine events
      // when the loaded track is ready to play
      soundcloud.addEventListener('onPlayerReady', function(flashId, data) {
        player = soundcloud.getPlayer(engineId);
        callbacks.onReady();
      });

      // when the loaded track finished playing
      soundcloud.addEventListener('onMediaEnd', callbacks.onEnd);

      // when the loaded track is still buffering
      soundcloud.addEventListener('onMediaBuffering', function(flashId, data) {
        callbacks.onBuffer(data.percent);
      });

      // when the loaded track started to play
      soundcloud.addEventListener('onMediaPlay', callbacks.onPlay);

      // when the loaded track is was paused
      soundcloud.addEventListener('onMediaPause', callbacks.onPause);

      return {
        load: function(track) {
          var url = track.uri;
          if(player){
            player.api_load(url);
          }else{
            // create a container for the flash engine (IE needs this to operate properly)
            $('<div class="sc-player-engine-container"></div>').appendTo(document.body).html(flashHtml(url));
          }
        },
        play: function() {
          player && player.api_play();
        },
        pause: function() {
          player && player.api_pause();
        },
        stop: function(){
          player && player.api_stop();
        },
        seek: function(relative){
          player && player.api_seekTo((player.api_getTrackDuration() * relative));
        },
        getDuration: function() {
          return player && player.api_getTrackDuration && player.api_getTrackDuration() * 1000;
        },
        getPosition: function() {
          return player && player.api_getTrackPosition && player.api_getTrackPosition() * 1000;
        },
        setVolume: function(val) {
          if(player && player.api_setVolume){
            player.api_setVolume(val);
          }
        }

      };
    };

    return html5AudioAvailable? html5Driver() : flashDriver();

  }();



  var apiKey,
      didAutoPlay = false,
      players = [],
      updates = {},
      currentUrl,
      loadTracksData = function($player, links, key) {
        var index = 0,
            playerObj = {node: $player, tracks: []},
            loadUrl = function(link) {
              var apiUrl = scApiUrl(link.url, apiKey);
              $.getJSON(apiUrl, function(data) {
                // log('data loaded', link.url, data);
                index += 1;
                if(data.tracks){
                  // log('data.tracks', data.tracks);
                  playerObj.tracks = playerObj.tracks.concat(data.tracks);
                }else if(data.duration){
                  // a secret link fix, till the SC API returns permalink with secret on secret response
                  data.permalink_url = link.url;
                  // if track, add to player
                  playerObj.tracks.push(data);
                }else if(data.creator){
                  // it's a group!
                  links.push({url:data.uri + '/tracks'});
                }else if(data.username){
                  // if user, get his tracks or favorites
                  if(/favorites/.test(link.url)){
                    links.push({url:data.uri + '/favorites'});
                  }else{
                    links.push({url:data.uri + '/tracks'});
                  }
                }else if($.isArray(data)){
                  playerObj.tracks = playerObj.tracks.concat(data);
                }
                if(links[index]){
                  // if there are more track to load, get them from the api
                  loadUrl(links[index]);
                }else{
                  // if loading finishes, anounce it to the GUI
                  playerObj.node.trigger({type:'onTrackDataLoaded', playerObj: playerObj, url: apiUrl});
                }
             });
           };
        // update current API key
        apiKey = key;
        // update the players queue
        players.push(playerObj);
        // load first tracks
        loadUrl(links[index]);
      },
      artworkImage = function(track, usePlaceholder) {
        if(usePlaceholder){
          return '<div class="sc-loading-artwork">Loading Artwork</div>';
        }else if (track.artwork_url) {
          return '<img src="' + track.artwork_url.replace('-large', '-t300x300') + '"/>';
        }else{
          return '<div class="sc-no-artwork">No Artwork</div>';
        }
      },
      updateTrackInfo = function($player, track) {
        // update the current track info in the player
        // log('updateTrackInfo', track);
        $('.sc-info', $player).each(function(index) {
          $('h3', this).html('<a href="' + track.permalink_url +'">' + track.title + '</a>');
          $('h4', this).html('by <a href="' + track.user.permalink_url +'">' + track.user.username + '</a>');
          $('p', this).html(track.description || 'no Description');
        });
        // update the artwork
        $('.sc-artwork-list li', $player).each(function(index) {
          var $item = $(this),
              itemTrack = $item.data('sc-track');

          if (itemTrack === track) {
            // show track artwork
            $item
              .addClass('active')
              .find('.sc-loading-artwork')
                .each(function(index) {
                  // if the image isn't loaded yet, do it now
                  $(this).removeClass('sc-loading-artwork').html(artworkImage(track, false));
                });
          }else{
            // reset other artworks
            $item.removeClass('active');
          }
        });
        // update the track duration in the progress bar
        $('.sc-duration', $player).html(timecode(track.duration));
        // put the waveform into the progress bar
        $('.sc-waveform-container', $player).html('<img src="' + track.waveform_url +'" />');

        $player.trigger('onPlayerTrackSwitch.scPlayer', [track]);
      },
      play = function(track) {
        var url = track.permalink_url;
        if(currentUrl === url){
          // log('will play');
          audioEngine.play();
        }else{
          currentUrl = url;
          // log('will load', url);
          audioEngine.load(track, apiKey);
        }
      },
      getPlayerData = function(node) {
        return players[$(node).data('sc-player').id];
      },
      updatePlayStatus = function(player, status) {
        if(status){
          // reset all other players playing status
          $('div.sc-player.playing').removeClass('playing');
        }
        $(player)
          .toggleClass('playing', status)
          .trigger((status ? 'onPlayerPlay' : 'onPlayerPause'));
      },
      onPlay = function(player, id) {
        var track = getPlayerData(player).tracks[id || 0];
        updateTrackInfo(player, track);
        // cache the references to most updated DOM nodes in the progress bar
        updates = {
          $buffer: $('.sc-buffer', player),
          $played: $('.sc-played', player),
          position:  $('.sc-position', player)[0]
        };
        updatePlayStatus(player, true);
        play(track);
      },
      onPause = function(player) {
        updatePlayStatus(player, false);
        audioEngine.pause();
      },
      onFinish = function() {
        var $player = updates.$played.closest('.sc-player'),
            $nextItem;
        // update the scrubber width
        updates.$played.css('width', '0%');
        // show the position in the track position counter
        updates.position.innerHTML = timecode(0);
        // reset the player state
        updatePlayStatus($player, false);
        // stop the audio
        audioEngine.stop();
        $player.trigger('onPlayerTrackFinish');
      },
      onSeek = function(player, relative) {
        audioEngine.seek(relative);
        $(player).trigger('onPlayerSeek');
      },
      onSkip = function(player) {
        var $player = $(player);
        // continue playing through all players
        log('track finished get the next one');
        $nextItem = $('.sc-trackslist li.active', $player).next('li');
        // try to find the next track in other player
        if(!$nextItem.length){
          $nextItem = $player.nextAll('div.sc-player:first').find('.sc-trackslist li.active');
        }
        $nextItem.click();
      },
      soundVolume = function() {
        var vol = 80,
            cooks = document.cookie.split(';'),
            volRx = new RegExp('scPlayer_volume=(\\d+)');
        for(var i in cooks){
          if(volRx.test(cooks[i])){
            vol = parseInt(cooks[i].match(volRx)[1], 10);
            break;
          }
        }
        return vol;
      }(),
      onVolume = function(volume) {
        var vol = Math.floor(volume);
        // save the volume in the cookie
        var date = new Date();
        date.setTime(date.getTime() + (365 * 24 * 60 * 60 * 1000));
        soundVolume = vol;
        document.cookie = ['scPlayer_volume=', vol, '; expires=', date.toUTCString(), '; path="/"'].join('');
        // update the volume in the engine
        audioEngine.setVolume(soundVolume);
      },
      positionPoll;

    // listen to audio engine events
    $doc
      .bind('scPlayer:onAudioReady', function(event) {
        log('onPlayerReady: audio engine is ready');
        audioEngine.play();
        // set initial volume
        onVolume(soundVolume);
      })
      // when the loaded track started to play
      .bind('scPlayer:onMediaPlay', function(event) {
        clearInterval(positionPoll);
        positionPoll = setInterval(function() {
          var duration = audioEngine.getDuration(),
              position = audioEngine.getPosition(),
              relative = (position / duration);

          // update the scrubber width
          updates.$played.css('width', (100 * relative) + '%');
          // show the position in the track position counter
          updates.position.innerHTML = timecode(position);
          // announce the track position to the DOM
          $doc.trigger({
            type: 'onMediaTimeUpdate.scPlayer',
            duration: duration,
            position: position,
            relative: relative
          });
        }, 500);
      })
      // when the loaded track is was paused
      .bind('scPlayer:onMediaPause', function(event) {
        clearInterval(positionPoll);
        positionPoll = null;
      })
      // change the volume
      .bind('scPlayer:onVolumeChange', function(event) {
        onVolume(event.volume);
      })
      .bind('scPlayer:onMediaEnd', function(event) {
        onFinish();
      })
      .bind('scPlayer:onMediaBuffering', function(event) {
        updates.$buffer.css('width', event.percent + '%');
      });


  // Generate custom skinnable HTML/CSS/JavaScript based SoundCloud players from links to SoundCloud resources
  $.scPlayer = function(options, node) {
    var opts = $.extend({}, $.scPlayer.defaults, options),
        playerId = players.length,
        $source = node && $(node),
        sourceClasses = $source[0].className.replace('sc-player', ''),
        links = opts.links || $.map($('a', $source).add($source.filter('a')), function(val) { return {url: val.href, title: val.innerHTML}; }),
        $player = $('<div class="sc-player loading"></div>').data('sc-player', {id: playerId}),
        $artworks = $('<ol class="sc-artwork-list"></ol>').appendTo($player),
        $controls = $('<div class="sc-controls"></div>').appendTo($player),
        $info = $('<div class="sc-info"><h3></h3><h4></h4><p></p><a href="#" class="sc-info-close">X</a></div>').appendTo($player);

        // add the classes of the source node to the player itself
        // the players can be indvidually styled this way
        if(sourceClasses || opts.customClass){
          $player.addClass(sourceClasses).addClass(opts.customClass);
        }


        // adding controls to the player
        $player
          .find('.sc-controls')
            .append('<a href="#play" class="sc-play">Play</a> <a href="#pause" class="sc-pause hidden">Pause</a>')
          .end()
          .append('<a href="#info" class="sc-info-toggle">Info</a>')
          .append('<div class="sc-scrubber"></div>')
            .find('.sc-scrubber')
              .append('<div class="sc-volume-slider"><span class="sc-volume-status" style="width:' + soundVolume +'%"></span></div>')
              .append('<div class="sc-time-span"><div class="sc-waveform-container"></div><div class="sc-buffer"></div><div class="sc-played"></div></div>')
              .append('<div class="sc-time-indicators"><span class="sc-position"></span> | <span class="sc-duration"></span></div>');
        $list = $('<ol class="sc-trackslist"></ol>').appendTo($player);
        // load and parse the track data from SoundCloud API
        loadTracksData($player, links, opts.apiKey);
        // init the player GUI, when the tracks data was laoded
        $player.bind('onTrackDataLoaded.scPlayer', function(event) {
          // log('onTrackDataLoaded.scPlayer', event.playerObj, playerId, event.target);
          var tracks = event.playerObj.tracks;
          if (opts.randomize) {
            tracks = shuffle(tracks);
          }
          // create the playlist
          $.each(tracks, function(index, track) {
            var active = index === 0;
            // create an item in the playlist
            $('<li><a href="' + track.permalink_url +'">' + track.title + '</a><span class="sc-track-duration">' + timecode(track.duration) + '</span></li>').data('sc-track', {id:index}).toggleClass('active', active).appendTo($list);
            // create an item in the artwork list
            $('<li></li>')
              .append(artworkImage(track, index >= opts.loadArtworks))
              .appendTo($artworks)
              .toggleClass('active', active)
              .data('sc-track', track);
          });
          // update the element before rendering it in the DOM
          $player.each(function() {
            if($.isFunction(opts.beforeRender)){
              opts.beforeRender.call(this, tracks);
            }
          });
          // set the first track's duration
          $('.sc-duration', $player)[0].innerHTML = timecode(tracks[0].duration);
          $('.sc-position', $player)[0].innerHTML = timecode(0);
          // set up the first track info
          updateTrackInfo($player, tracks[0]);

          // if continous play enabled always skip to the next track after one finishes
          if (opts.continuePlayback) {
            $player.bind('onPlayerTrackFinish', function(event) {
              onSkip($player);
            });
          }

          // announce the succesful initialization
          $player
            .removeClass('loading')
            .trigger('onPlayerInit');

          // if auto play is enabled and it's the first player, start playing
          if(opts.autoPlay && !didAutoPlay){
            onPlay($player);
            didAutoPlay = true;
          }
        });


    // replace the DOM source (if there's one)
    $source.each(function(index) {
      $(this).replaceWith($player);
    });

    return $player;
  };

  // stop all players, might be useful, before replacing the player dynamically
  $.scPlayer.stopAll = function() {
    $('.sc-player.playing a.sc-pause').click();
  };

  // destroy all the players and audio engine, usefull when reloading part of the page and audio has to stop
  $.scPlayer.destroy = function() {
    $('.sc-player, .sc-player-engine-container').remove();
  };

  // plugin wrapper
  $.fn.scPlayer = function(options) {
    // reset the auto play
    didAutoPlay = false;
    // create the players
    this.each(function() {
      $.scPlayer(options, this);
    });
    return this;
  };

  // default plugin options
  $.scPlayer.defaults = $.fn.scPlayer.defaults = {
    customClass: null,
    // do something with the dom object before you render it, add nodes, get more data from the services etc.
    beforeRender  :   function(tracksData) {
      var $player = $(this);
    },
    // initialization, when dom is ready
    onDomReady  : function() {
      $('a.sc-player, div.sc-player').scPlayer();
    },
    autoPlay: false,
    continuePlayback: true,
    randomize: false,
    loadArtworks: 5,
    // the default Api key should be replaced by your own one
    // get it here https://soundcloud.com/you/apps/new
    apiKey: 'htuiRd1JP11Ww0X72T1C3g'
  };


  // the GUI event bindings
  //--------------------------------------------------------

  // toggling play/pause
  $(document).on('click','a.sc-play, a.sc-pause', function(event) {
    var $list = $(this).closest('.sc-player').find('ol.sc-trackslist');
    // simulate the click in the tracklist
    $list.find('li.active').click();
    return false;
  });

  // displaying the info panel in the player
  $(document).on('click','a.sc-info-toggle, a.sc-info-close', function(event) {
    var $link = $(this);
    $link.closest('.sc-player')
      .find('.sc-info').toggleClass('active').end()
      .find('a.sc-info-toggle').toggleClass('active');
    return false;
  });

  // selecting tracks in the playlist
  $(document).on('click','.sc-trackslist li', function(event) {
    var $track = $(this),
        $player = $track.closest('.sc-player'),
        trackId = $track.data('sc-track').id,
        play = $player.is(':not(.playing)') || $track.is(':not(.active)');
    if (play) {
      onPlay($player, trackId);
    }else{
      onPause($player);
    }
    $track.addClass('active').siblings('li').removeClass('active');
    $('.artworks li', $player).each(function(index) {
      $(this).toggleClass('active', index === trackId);
    });
    return false;
  });

  var scrub = function(node, xPos) {
    var $scrubber = $(node).closest('.sc-time-span'),
        $buffer = $scrubber.find('.sc-buffer'),
        $available = $scrubber.find('.sc-waveform-container img'),
        $player = $scrubber.closest('.sc-player'),
        relative = Math.min($buffer.width(), (xPos  - $available.offset().left)) / $available.width();
    onSeek($player, relative);
  };

  var onTouchMove = function(ev) {
    if (ev.targetTouches.length === 1) {
      scrub(ev.target, ev.targetTouches && ev.targetTouches.length && ev.targetTouches[0].clientX);
      ev.preventDefault();
    }
  };


  // seeking in the loaded track buffer
  $(document)
    .on('click','.sc-time-span', function(event) {
      scrub(this, event.pageX);
      return false;
    })
    .on('touchstart','.sc-time-span', function(event) {
      this.addEventListener('touchmove', onTouchMove, false);
      event.originalEvent.preventDefault();
    })
    .on('touchend','.sc-time-span', function(event) {
      this.removeEventListener('touchmove', onTouchMove, false);
      event.originalEvent.preventDefault();
    });

  // changing volume in the player
  var startVolumeTracking = function(node, startEvent) {
    var $node = $(node),
        originX = $node.offset().left,
        originWidth = $node.width(),
        getVolume = function(x) {
          return Math.floor(((x - originX)/originWidth)*100);
        },
        update = function(event) {
          $doc.trigger({type: 'scPlayer:onVolumeChange', volume: getVolume(event.pageX)});
        };
    $node.bind('mousemove.sc-player', update);
    update(startEvent);
  };

  var stopVolumeTracking = function(node, event) {
    $(node).unbind('mousemove.sc-player');
  };

  $(document)
    .on('mousedown','.sc-volume-slider', function(event) {
      startVolumeTracking(this, event);
    })
    .on('mouseup','.sc-volume-slider', function(event) {
      stopVolumeTracking(this, event);
    });

  $doc.bind('scPlayer:onVolumeChange', function(event) {
    $('span.sc-volume-status').css({width: event.volume + '%'});
  });
  // -------------------------------------------------------------------

  // the default Auto-Initialization
  $(function() {
    if($.isFunction($.scPlayer.defaults.onDomReady)){
      $.scPlayer.defaults.onDomReady();
    }
  });

})(jQuery);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
  2. https://s3-us-west-2.amazonaws.com/s.cdpn.io/35376/soundcloud.player.api.js