<!-- design inspired by https://dribbble.com/shots/4290719-Player -->
<!-- smooth parallax https://css-tricks.com/animated-intro-rxjs/ -->
<div class="toggles">
  <button class="toggle--dark">
    Dark Theme
  </button>
  <button class="toggle-light">
    Light Theme
  </button>
</div>

<div class="album">
  <svg class="album__menu" height="12px" version="1.1" viewBox="0 0 18 12" width="18px"><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="#000000" id="Core" transform="translate(-87.000000, -342.000000)"><g id="menu" transform="translate(87.000000, 342.000000)"><path d="M0,12 L18,12 L18,10 L0,10 L0,12 L0,12 Z M0,7 L18,7 L18,5 L0,5 L0,7 L0,7 Z M0,0 L0,2 L18,2 L18,0 L0,0 L0,0 Z" id="Shape"/></g></g></g></svg>
  <div class="album__reflection"></div>
  <div class="album__player">
    <div class="album__song">
      <h2 class="album__song__album">
        <marquee scrollamount="3">
          <!-- SOMEBODY STOP ME -->
          Grouper - Grid of Points
        </marquee>
      </h2>
      <h1 class="album__song__title">
        The Races
      </h1>
      <h2 class="album__song__position">
        track 01 of 07
      </h2>
    </div>

    <div class="album__controls">

      <svg class="album__prev" height="12px" viewBox="0 0 18 12" width="18px"><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="#000000" id="Icons-AV" transform="translate(-43.000000, -5.000000)"><g id="fast-forward" transform="translate(43.000000, 5.000000)"><path d="M0,12 L8.5,6 L0,0 L0,12 L0,12 Z M9,0 L9,12 L17.5,6 L9,0 L9,0 Z" id="Shape"/></g></g></g></svg>

      <svg class="album__play" height="20" viewBox="0 0 48 48" width="20" xmlns="http://www.w3.org/2000/svg"><path d="M-838-2232H562v3600H-838z" fill="none"/><path d="M16 10v28l22-14z"/><path d="M0 0h48v48H0z" fill="none"/></svg>

      <svg class="album__next" height="12px" viewBox="0 0 18 12" width="18px"><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="#000000" id="Icons-AV" transform="translate(-43.000000, -5.000000)"><g id="fast-forward" transform="translate(43.000000, 5.000000)"><path d="M0,12 L8.5,6 L0,0 L0,12 L0,12 Z M9,0 L9,12 L17.5,6 L9,0 L9,0 Z" id="Shape"/></g></g></g></svg>

    </div>
    <div class="album__song__scrubber">
      <input type="range" min="0" max="100" class="album__song__scrubber">
      <span class="album__song__current-time">0:28</span>
      <span class="album__song__full-length">0:50</span>
    </div>

  </div>

  <!--   <img src="https://f4.bcbits.com/img/a3330461814_10.jpg" alt="" class="album__art"> -->
  <div class="album__art"></div>

  <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/36124/profile/profile-80.jpg?1520103364" class="album__avatar">

  <div class="album__interactions">
    <svg enable-background="new 0 0 128 128" height="12px" id="Layer_1" version="1.1" viewBox="0 0 128 128" width="12px" class="album__like"><path d="M127,44.205c0-18.395-14.913-33.308-33.307-33.308c-12.979,0-24.199,7.441-29.692,18.276  c-5.497-10.835-16.714-18.274-29.694-18.274C15.912,10.898,1,25.81,1,44.205C1,79,56.879,117.104,64.001,117.104  C71.124,117.104,127,79.167,127,44.205z" fill="#232323"/></svg>

    <svg enable-background="new 0 0 24 24" height="12px" id="Layer_1" version="1.1" viewBox="0 0 24 24" width="12px" class="album__add"><path clip-rule="evenodd" d="M22.5,14H14v8.5c0,0.276-0.224,0.5-0.5,0.5h-4C9.224,23,9,22.776,9,22.5V14H0.5  C0.224,14,0,13.776,0,13.5v-4C0,9.224,0.224,9,0.5,9H9V0.5C9,0.224,9.224,0,9.5,0h4C13.776,0,14,0.224,14,0.5V9h8.5  C22.776,9,23,9.224,23,9.5v4C23,13.776,22.776,14,22.5,14z" fill-rule="evenodd"/></svg>
  </div>

  <svg class="album__volume" enable-background="new 0 0 512 512" id="Layer_1" version="1.1" viewBox="0 0 512 512" xml:space="preserve" width="20" height="20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><path d="M114.8,368.1H32.1c-5.8,0-10.5-4.7-10.5-10.5V154.4c0-5.8,4.7-10.5,10.5-10.5h82.7   c5.8,0,10.5,4.7,10.5,10.5v203.2C125.4,363.4,120.7,368.1,114.8,368.1z M42.7,347.1h61.6V165H42.7V347.1z" fill="#6A6E7C"/><path d="M303.7,512c-2.3,0-4.5-0.7-6.4-2.2L108.4,366c-2.6-2-4.2-5.1-4.2-8.4V154.4c0-3.3,1.5-6.4,4.2-8.4   L297.3,2.2c3.2-2.4,7.5-2.8,11.1-1.1c3.6,1.8,5.9,5.4,5.9,9.5v490.9c0,4-2.3,7.7-5.9,9.5C306.8,511.6,305.2,512,303.7,512z    M125.4,352.4l167.7,127.8V31.8L125.4,159.6V352.4z" fill="#6A6E7C"/><path d="M393.6,334.9c-5.8,0-10.5-4.7-10.5-10.5V187.7c0-5.8,4.7-10.5,10.5-10.5c5.8,0,10.5,4.7,10.5,10.5v136.7   C404.1,330.2,399.4,334.9,393.6,334.9z" fill="#6A6E7C"/><path d="M479.9,392.4c-5.8,0-10.5-4.7-10.5-10.5V130.1c0-5.8,4.7-10.5,10.5-10.5c5.8,0,10.5,4.7,10.5,10.5v251.7   C490.4,387.7,485.7,392.4,479.9,392.4z" fill="#6A6E7C"/></g></svg>

</div>
@import url("https://fonts.googleapis.com/css?family=Arimo:400,400i,700,900");

// reset range
input[type="range"] {
  -webkit-appearance: none; /* Hides the slider so that custom slider can be made */
  width: 100%; /* Specific width is required for Firefox. */
  background: transparent; /* Otherwise white in Chrome */
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
}

input[type="range"]:focus {
  outline: none; /* Removes the blue border. You should probably do some kind of focus styling for accessibility reasons though. */
}

input[type="range"]::-ms-track {
  width: 100%;
  cursor: pointer;

  /* Hides the slider so custom styles can be added */
  background: transparent;
  border-color: transparent;
  color: transparent;
}

:root {
  --width: 60vw;
  --highlight: #fdfdfd;
  --background: #1d1d1d;
  --accent: #f70040;
  --easing: cubic-bezier(0.645, 0.045, 0.355, 1);
  --move-x: 0deg;
  --move-y: 0deg;

  @media screen and (max-width: 1090px) {
    --width: 80vw;
  }

  @media screen and (max-width: 786px) {
    --width: 90vw;
  }
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  width: 100%;
  min-height: 100vh;
  background: #f6f6f6; //ffecef
  perspective: 90vw;
}

.toggles {
  width: 100%;
  text-align: center;
  margin: -25px 0 25px;
}

.album {
  // 685 x 390
  position: relative;
  width: var(--width);
  height: 0;
  box-shadow: 0px 50px 40px -35px rgba(0, 0, 0, 0.3);
  background: var(--background);
  display: grid;
  grid-template-columns: 30% 60% 10%;
  grid-template-rows: repeat(8, 1fr);
  transform-style: preserve-3d;
  transition: all 0.6s linear;
  min-width: 685px;
  max-width: 810px;
  min-height: 390px;
  max-height: 500px;
  transform: rotateX(-45deg);

  &.album--loaded {
    transform: rotateX(0);
    height: calc(var(--width) * 0.6);
  }

  &.album--parallax {
    // transition: all 0.1s linear; // this kills older machines
    transition: none;
    transform: rotateX(var(--move-y)) rotateY(var(--move-x));
  }
}

.album__menu {
  width: 20px;
  margin-top: 30px;
  margin-left: 30px;

  path {
    fill: var(--highlight);
  }
}

.album__reflection {
  position: absolute;
  width: 60%;
  top: 0;
  left: 30%;
  bottom: 0;
  box-shadow: 0px 30px 40px -15px rgba(0, 0, 0, 0.7);
}

.album__player {
  position: relative;
  display: contents;
  grid-column: 1 / 1;
  grid-row: 1 / -1;
  z-index: 3;
}

.album__art {
  position: relative;
  grid-column: 2 / 2;
  grid-row: 1 / -1;
  display: block;
  background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/36124/grouper-grid-of-points.jpg")
    50% 50% no-repeat;
  background-size: cover !important;
  max-width: 100%;
  transform: scale(0);
  transition: all 0.6s 0.3s var(--easing);
  z-index: 2;
  max-height: 100%;

  .album--loaded & {
    transform: scale(1);
  }
}

.album__song {
  grid-row: 2 / 3;
  grid-column: 1 / 2;
  margin-left: 40px;
  font-family: "Arimo", sans-serif;
  transform: translateZ(50px);
  z-index: 3;
}

.album__song__album,
.album__song__position {
  color: var(--highlight);
  font-size: 12px;
  margin: 10px 0;
  transform: translateZ(35px) translateX(-100%);
  opacity: 0;
  transition: all 0.6s 0.6s var(--easing);
  .album--loaded & {
    opacity: 1;
    transform: translateZ(35px) translateX(0);
  }
}

.album__song__album {
  margin-right: 20px;
  margin-left: 5px;
}

.album__song__title {
  font-size: calc(100vw * 0.07); // *shrug*
  color: var(--accent);
  line-height: 0.9;
  font-weight: 900;
  transform: translateX(-100%);
  opacity: 0;
  z-index: 3;
  transition: all 0.3s 0.6s var(--easing);
  .album--loaded & {
    opacity: 1;
    transform: translateX(0);
  }
  .album--parallax & {
    transition: all 0.1s linear;
  }
}

.album__controls {
  grid-row: 6 / 7;
  grid-column: 1 / 2;
  padding-left: 40px;
  display: flex;
  align-items: center;

  transform: translateZ(35px) translateX(-100%);
  opacity: 0;
  transition: all 0.8s 0.6s var(--easing);

  .album--loaded & {
    opacity: 1;
    transform: translateZ(35px) translateX(0);
  }
}

.album__song__scrubber {
  grid-row: 7;
  width: 70%;
  margin: 10px auto 0;

  transform: translateZ(35px) translateX(-100%);
  opacity: 0;
  transition: all 0.8s 0.6s var(--easing);

  .album--loaded & {
    opacity: 1;
    transform: translateZ(35px) translateX(0);
  }
}

.album__song__current-time,
.album__song__full-length {
  color: var(--highlight);
  font-size: 10px;
  font-family: "Arimo", sans-serif;
  display: block;
  margin-top: 5px;
}

.album__song__current-time {
  float: left;
}

.album__song__full-length {
  float: right;
}

// range is a pain
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  height: 5px;
  width: 5px;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
  margin-top: -2px; /* You need to specify a margin in Chrome, but in Firefox and IE it is automatic */
}
input[type=range]::-moz-range-thumb {
  -webkit-appearance: none;
  height: 5px;
  width: 5px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  cursor: pointer;
  margin-top: -2px; /* You need to specify a margin in Chrome, but in Firefox and IE it is automatic */
}


input[type="range"]::-webkit-slider-runnable-track {
  height: 1px;
  background: var(--highlight);
  width: 100%;
}

input[type=range]::-moz-range-track {
  height: 1px;
  background: var(--highlight);
  width: 100%;
}

input[type="range"]:focus::-webkit-slider-runnable-track {
  background: var(--highlight);
}

.album__prev,
.album__play {
  margin-right: 13px;
}

.album__prev,
.album__next {
  transform: translateZ(10px);
  path {
    fill: var(--highlight);
  }
}

.album__prev {
  transform-origin: 50%;
  transform: rotateZ(180deg);
}

.album__play {
  padding: 10px;
  border: 2px solid var(--accent);
  border-radius: 50%;
  cursor: pointer;
  transform: translateZ(10px);
  path {
    transform-origin: 50%;
  }
  path:nth-of-type(2) {
    fill: var(--accent);
  }

  &:hover {
    border: 2px solid var(--highlight);
    path {
      transform: scale(1.2);
    }
  }
}

.album__avatar {
  width: 45px;
  height: 45px;
  grid-column: 3 / 4;
  border-radius: 50%;
  margin: 25px 0 0 15px;
  transition: all 0.6s 0.3s var(--easing);
  transform: translateY(25%) scale(0.3);
  transform-origin: 50% 50%;
  opacity: 0;

  .album--loaded & {
    // transition: filter 1s linear;
    transform: translateY(0) scale(1);
    opacity: 1;
  }

  @media screen and (max-width: 850px) {
    width: 30px;
    height: 30px;
    margin-left: 25px;
  }
}

.album__like {
  margin: 15px 27px;
  cursor: pointer;
  padding: 5px;
  border: 1px solid var(--accent);
  border-radius: 50%;
  transition: all 0.6s 0.6s var(--easing);
  transform: translateY(25%) scale(0.3);
  transform-origin: 50% 50%;
  opacity: 0;
  path {
    fill: var(--accent);
  }
  &:hover {
    border-color: var(--highlight);
    path {
      fill: var(--highlight);
    }
  }
  .album--loaded & {
    transform: scale(1) translateY(0);
    opacity: 1;
  }
  .album--parallax & {
    transition: all 0.1s linear;
  }
}

.album__add {
  margin: 0 27px;
  cursor: pointer;
  padding: 5px;
  border: 1px solid #979797;
  border-radius: 50%;
  transition: all 0.6s 0.9s var(--easing);
  transform: translateY(25%) scale(0.3);
  transform-origin: 50% 50%;
  opacity: 0;
  path {
    fill: var(--highlight);
  }
  &:hover {
    border: 1px solid var(--accent);
    path {
      fill: var(--accent);
    }
  }
  .album--loaded & {
    transform: scale(1) translateY(0);
    opacity: 1;
  }
}

.album__volume {
  grid-column: 3 / 4;
  grid-row: 7 / -1;
  transform-origin: 50%;
  transform: rotateZ(-90deg);
  margin: 15px 0 0 25px;
  transition: all 0.6s 1.2s var(--easing);
  transform: translateY(25%) scale(0.3);
  transform-origin: 50% 50%;
  opacity: 0;
  path {
    stroke: var(--highlight);
    fill: var(--highlight);
    stroke-width: 10px;
  }
  .album--loaded & {
    transform: scale(1) translateY(0);
    opacity: 1;
  }
}
View Compiled
const body = document.querySelector("body");
const album = document.querySelector(".album");
let loaded = false;
const albumDetails = {
  dark: {
    album: "Grouper - Grid of Points",
    song: "The Races",
    art:
      "https://s3-us-west-2.amazonaws.com/s.cdpn.io/36124/grouper-grid-of-points.jpg",
    position: "01 of 07",
    start: "0:27",
    end: "0:50",
    highlight: "#fdfdfd",
    accent: "#f70040",
    background: "#1d1d1d"
  },
  light: {
    album: "Frontierer - Orange Mathematics",
    song: "The Collapse",
    art:
      "https://s3-us-west-2.amazonaws.com/s.cdpn.io/36124/frontierer-orange-mathematics.jpg",
    accent: "#f78900",
    highlight: "#1d1d1d",
    background: "#fdfdfd",
    position: "03 of 16",
    start: "1:00",
    end: "2:08"
  }
};
album.classList.add("album--loaded");

setTimeout(() => {
  album.classList.add("album--parallax");
}, 1200);

function toggleThemes(theme) {
  const album = albumDetails[theme];
  const albumArt = document.querySelector(".album__art");
  const albumTitle = document.querySelector(".album__song__album marquee");
  const albumSong = document.querySelector(".album__song__title");
  const albumPosition = document.querySelector(".album__song__position");
  const albumCurrentTime = document.querySelector(".album__song__current-time");
  const albumFullTime = document.querySelector(".album__song__full-length");
  albumArt.style.background = `url(${album.art}) 50% 50% no-repeat`;
  albumTitle.innerText = album.album;
  albumSong.innerText = album.song;
  albumPosition.innerText = album.position;
  albumCurrentTime.innerText = album.start;
  albumFullTime.innerText = album.end;
  document.documentElement.style.setProperty("--highlight", album.highlight);
  document.documentElement.style.setProperty("--background", album.background);
  document.documentElement.style.setProperty("--accent", album.accent);
}

const buttons = document.querySelectorAll(".toggles button");
buttons.forEach(function(el) {
  el.addEventListener("click", () => {
    let theme = "";
    if (el.innerText === "Dark Theme") {
      theme = "dark";
    } else {
      theme = "light";
    }
    toggleThemes(theme);
  });
});

// https://css-tricks.com/animated-intro-rxjs/

function smoothParallax() {
  const body = document.querySelector("body");
  const clientHeight = window.innerHeight;
  const bodyDims = {
    w: body.getBoundingClientRect().width,
    h: body.getBoundingClientRect().height
  };
  const limit = {
    x: 25,
    y: 25
  };
  console.clear();
  
  function lerp(start, end) {
    const dx = end.x - start.x;
    const dy = end.y - start.y;
    
    return {
      x: start.x + dx * 0.1,
      y: start.y + dy * 0.1
    };
  }
  
  const docEl = document.documentElement;  
  
  const mouseMove$ = Rx.Observable
    .fromEvent(docEl, "mousemove")
    .map(event => ({
      x: event.clientX,
      y: event.clientY
  }));
  
  const touchMove$ = Rx.Observable
    .fromEvent(docEl, "touchmove")
    .map(event => ({
      x: event.touches[0].clientX,
      y: event.touches[0].clientY
  }));
  
  const animationFrame$ = Rx.Observable.interval(10, Rx.Scheduler.animationFrame);
  
  const move$ = Rx.Observable.merge(mouseMove$, touchMove$);
  
  const smoothMove$ = animationFrame$
    .withLatestFrom(move$, (frame, move) => move)
    .scan(lerp);
  
  console.log(smoothMove$);
  
  
  
  smoothMove$.subscribe(pos => {
    const clamped = {
      x: pos.x / bodyDims.w * limit.x - limit.x / 2,
      y: pos.y / bodyDims.h * limit.y - limit.y / 2
    };
    document.documentElement.style.setProperty("--move-y", `${clamped.y}deg`);
    document.documentElement.style.setProperty("--move-x", `${clamped.x}deg`);
    
  });
}

smoothParallax();
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/@reactivex/rxjs@5.0.0-beta.1/dist/global/Rx.umd.js