Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

HTML

              
                <!DOCTYPE html>
<html>

<head>
  <title></title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>

<body>
  <aside>
    <!-- cassette -->
    <div class="cards">
      <div class="cassette card" id="cassette-01">
        <i class="pin-left-top"></i>
        <i class="pin-right-top"></i>
        <i class="pin-left-bottom"></i>
        <i class="pin-right-bottom"></i>
        <div class="cassette-inner">
          <div class="cassette-content">
            <div class="cassette-info">
              <span class="logo">IC</span>
              <span class="title">《半生缘》</span>
            </div>
            <div class="cassette-tape-container">
              <div class="cassette-tape-left">
                <div class="cassette-circle-outer">
                  <div class="cassette-circle-inner">
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                  </div>
                </div>
              </div>
              <div class="cassette-tape-right">
                <div class="cassette-circle-outer">
                  <div class="cassette-circle-inner">
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div class="cassette-footer">

          </div>
        </div>

      </div>
      <div class="cassette card" id="cassette-02">
        <i class="pin-left-top"></i>
        <i class="pin-right-top"></i>
        <i class="pin-left-bottom"></i>
        <i class="pin-right-bottom"></i>
        <div class="cassette-inner">
          <div class="cassette-content">
            <div class="cassette-info">
              <span class="logo">IC</span>
              <span class="title">A Little Braver</span>
            </div>
            <div class="cassette-tape-container">
              <div class="cassette-tape-left">
                <div class="cassette-circle-outer">
                  <div class="cassette-circle-inner">
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                  </div>
                </div>
              </div>
              <div class="cassette-tape-right">
                <div class="cassette-circle-outer">
                  <div class="cassette-circle-inner">
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                    <i class="sawtooth"></i>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div class="cassette-footer">

          </div>
        </div>

      </div>
    </div>
  </aside>
  <div id="info-circle" onclick="this.style.transform='scale3d(0, 0, 0)'">
    <div>Click Left</div>
    <div>To Play</div>
  </div>
  <main>
    <!-- boom box -->
    <div class="boom-box">
      <!-- boom handler -->
      <div class="boom-handler"></div>
      <!-- boom container -->
      <div class="boom-player">
        <!-- boom nav -->
        <div class="boom-nav">
          <!-- controls -->
          <div class="boom-controls">
            <div class="boom-contrl-range">
              <input type="range" id="volume" min="0" max="2" value="1" list="gain-vals" step="0.1" data-action="volume" />
              <datalist id="gain-vals">
                                <option value="0" label="min">
                                <option value="2" label="max">
                            </datalist>
              <label for="volume">VOL</label>
            </div>
            <div class="boom-contrl-range">
              <input type="range" id="panner" list="pan-vals" min="-1" max="1" value="0" step="0.1" data-action="panner" />
              <datalist id="pan-vals">
                                <option value="-1" label="left">
                                <option value="1" label="right">
                            </datalist>
              <label for="panner">PAN</label>
            </div>
          </div>
          <div class="boom-data">
            <canvas class="audio-analyse"></canvas>
          </div>
        </div>

        <!-- boom bottom -->
        <div class="boom-bottom">
          <div class="boom-circle">
            <div class="boom-circle-inner"></div>
          </div>
          <fieldset class="cassette-outer">
            <legend>AUTIO CASSETTE PLAYER</legend>
            <div class="cassette-box">
              <div class="cassette-tape-real"></div>
            </div>
            <div>
              <button class="control-play">Play / Pause</button>
            </div>
          </fieldset>
          <div class="boom-circle">
            <div class="boom-circle-inner"></div>
          </div>
        </div>
      </div>
    </div>
  </main>
  <script type="application/javascript">
    const audioList = [{
        src: 'https://epherema.oss-cn-hongkong.aliyuncs.com/music/music.mp3',
        type: 'audio/mp3'
      },
      {
        src: 'https://epherema.oss-cn-hongkong.aliyuncs.com/music/New%20Empire%20-%20A%20Little%20Braver.mp3',
        type: 'audio/mp3'
      }
    ]
    window.onload = function() {
      let av = new AudioVisualize({
          playList: audioList,
          playButton: '.control-play',
          volControl: '#volume',
          panControl: '#panner',
          visualCanvas: '.audio-analyse'
        }),
        cards = document.querySelectorAll('.cards .card'),
        box = document.querySelector('.cassette-box'),
        tapeBox,
        curAudio = '',
        cloneCards = {};

      function reset(index) {
        av.changeAudio(index);
        curAudio = index;
      }
      cards.forEach(function(card, index) {
        cloneCards[index] = card.cloneNode(true);
        card.addEventListener('click', (function(index) {
          return function(e) {
            if (index !== curAudio) {
              let tapeBox = document.querySelector('.cassette-box > div');
              let target = cloneCards[index];
              box.replaceChild(target, tapeBox);
              reset(index);
            }
          }
        })(index), false);
      })
    }
    window.onbeforeunload = function() {
      av = null;
    }
  </script>
</body>

</html>
              
            
!

CSS

              
                :root {
    --main-color: #333;
    --border-color: #fff;
    --cassette-size: 180px;
    --border-weight: 2px;
}

* {
    margin: 0;
    padding: 0;
}

html,
body {
    width: 100%;
    height: 100%;
}

body {
    font-family: cursive, 'Courier New', Helvetica, serif;
    font-size: 14px;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    background: var(--main-color);
    color: var(--border-color);
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    /* Firefox */
    -webkit-box-sizing: border-box;
    /* Safari */
}

/* layoout */
aside {
    margin-top: 20px;
    -ms-flex-preferred-size: calc(var(--cassette-size) * 1.2);
    flex-basis: calc(var(--cassette-size) * 1.2);
    -webkit-box-flex: 0;
    -ms-flex-positive: 0;
    flex-grow: 0;
    -ms-flex-negative: 0;
    flex-shrink: 0;
}

main {
    margin-top: 20px;
    -webkit-box-flex: 1;
    -ms-flex: auto;
    flex: auto;
}

aside .cassette {
    margin: 20px auto;
}

#info-circle {
    position: fixed;
    top: 40px;
    left: calc(var(--cassette-size) * 1.2);
    width: 60px;
    height: 60px;
    padding: 10px;
    border-radius: 50%;
    border: dashed var(--border-weight) var(--border-color);
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    cursor: pointer;
    transition: transform 1000ms ease-in;
    background-color: var(--main-color);
    z-index: 99;
}
#info-circle > div {
    width: 80px;
    font-size: 14px;
    text-align: center;
    cursor: pointer;
}


/* cassette */
.cassette {
    margin: auto auto;
    position: relative;
    color: var(--border-color);
    width: var(--cassette-size);
    height: calc(var(--cassette-size) / 9 * 6);
    border: var(--border-weight) solid var(--border-color);
    border-radius: var(--border-weight);
    -webkit-border-radius: var(--border-weight);
    -moz-border-radius: var(--border-weight);
    -ms-border-radius: var(--border-weight);
    -o-border-radius: var(--border-weight);
    -webkit-box-shadow: 0 0 10px lightgray;
    box-shadow: 0 0 10px lightgray;
    cursor: pointer;
}

.cards .cassette:hover {
    -webkit-transform: scale3d(1.05, 1.05, 1) translate3d(0, 0, 0);
    -moz-transform: scale3d(1.05, 1.05, 1) translate3d(0, 0, 0);
    -ms-transform: scale3d(1.05, 1.05, 1) translate3d(0, 0, 0);
    -o-transform: scale3d(1.05, 1.05, 1) translate3d(0, 0, 0);
}


.cassette i[class|="pin"] {
    position: absolute;
    width: 5px;
    height: 5px;
    border: 2px solid var(--border-color);
    border-radius: 50%;
    margin: 2px 2px;
}

.cassette i[class|="pin"][class*="-left"] {
    left: 0px;
}

.cassette i[class|="pin"][class*="-right"] {
    right: 0px;
}

.cassette i[class|="pin"][class*="-bottom"] {
    bottom: 0px;
}

.cassette i[class|="pin"][class*="-top"] {
    top: 0px;
}

.cassette .cassette-inner {
    width: 90%;
    height: 90%;
    margin: 5% auto 5% auto;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-box-pack: space-around;
    -ms-flex-pack: space-around;
    justify-content: space-around;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
}

.cassette-inner .cassette-content {
    width: 100%;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    -ms-flex-preferred-size: 60%;
    flex-basis: 70%;
    -webkit-box-flex: 0;
    -ms-flex-positive: 0;
    flex-grow: 0;
    -ms-flex-negative: 0;
    flex-shrink: 0;
    border: var(--border-weight) solid var(--border-color);
    border-radius: var(--border-weight);
    -webkit-border-radius: var(--border-weight);
    -moz-border-radius: var(--border-weight);
    -ms-border-radius: var(--border-weight);
    -o-border-radius: var(--border-weight);
}

.cassette-content .cassette-info {
    text-align: left;
    width: 90%;
    height: 25px;
    margin: 2px auto;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
}

.cassette-content .cassette-info span {
    display: inline-block;
    font-size: 14px;
    vertical-align: middle;
}

.cassette-content .cassette-info .logo {
    width: 20px;
    height: 20px;
    border: 2px solid var(--border-color);
    line-height: 20px;
    text-align: center;
}

.cassette-content .cassette-info .title {
    width: 100%;
    height: 20px;
    line-height: 20px;
    background-image: -webkit-gradient(linear, left bottom, left top, from(var(--border-color)), color-stop(20%, transparent));
    background-image: linear-gradient(0deg, var(--border-color) 0%, transparent 20%);
    background-size: 100% 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
}

.cassette-content .cassette-tape-container {
    position: relative;
    width: 90%;
    height: 60%;
    margin: 2px auto;
    border: var(--border-weight) solid var(--border-color);
    border-radius: var(--border-weight);
    -webkit-border-radius: var(--border-weight);
    -moz-border-radius: var(--border-weight);
    -ms-border-radius: var(--border-weight);
    -o-border-radius: var(--border-weight);
    overflow: hidden;
}

.cassette-tape-container div[class|="cassette-tape"] {
    position: absolute;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    width: calc(var(--cassette-size) * .15);
    height: calc(var(--cassette-size) * .15);
    border: calc(var(--cassette-size) * .15) solid var(--border-color);
    border-radius: 50%;
    -webkit-border-radius: 50%;
    -moz-border-radius: 50%;
    -ms-border-radius: 50%;
    -o-border-radius: 50%;
}

.cassette-tape-container div[class|="cassette-tape"][class*="-left"] {
    top: 0;
    left: 0;
    transform: translate3d(-20px, calc(0px - var(--cassette-size) * .1), 0);
    -webkit-transform: translate3d(-20px, calc(0px - var(--cassette-size) * .1), 0);
    -moz-transform: translate3d(-20px, calc(0px - var(--cassette-size) * .1), 0);
    -ms-transform: translate3d(-20px, calc(0px - var(--cassette-size) * .1), 0);
    -o-transform: translate3d(-20px, calc(0px - var(--cassette-size) * .1), 0);
}

.cassette-tape-container div[class|="cassette-tape"] .cassette-circle-outer {
    width: 22px;
    height: 22px;
    border: var(--border-weight) solid var(--border-color);
    border-radius: 50%;
    -webkit-border-radius: 50%;
    -moz-border-radius: 50%;
    -ms-border-radius: 50%;
    -o-border-radius: 50%;
}

.cassette-tape-container div[class|="cassette-tape"][class*="-right"] {
    top: 0;
    right: 0;
    transform: translate3d(20px, calc(0px - var(--cassette-size) * .1), 0);
    -webkit-transform: translate3d(20px, calc(0px - var(--cassette-size) * .1), 0);
    -moz-transform: translate3d(20px, calc(0px - var(--cassette-size) * .1), 0);
    -ms-transform: translate3d(20px, calc(0px - var(--cassette-size) * .1), 0);
    -o-transform: translate3d(20px, calc(0px - var(--cassette-size) * .1), 0);
}

.cassette-tape-container .cassette-circle-inner {
    position: relative;
    width: 18px;
    height: 18px;
}

.cassette-tape-container .cassette-circle-inner .sawtooth {
    position: absolute;
    display: inline-block;
    top: 9px;
    left: 9px;
    width: 4px;
    height: 4px;
    background-color: var(--border-color);
}


.cassette .cassette-footer {
    width: 70%;
    -ms-flex-preferred-size: 15%;
    flex-basis: 15%;
    -webkit-box-flex: 0;
    -ms-flex-positive: 0;
    flex-grow: 0;
    -ms-flex-negative: 0;
    flex-shrink: 0;
    border: var(--border-weight) solid var(--border-color);
    border-radius: var(--border-weight);
    -webkit-border-radius: var(--border-weight);
    -moz-border-radius: var(--border-weight);
    -ms-border-radius: var(--border-weight);
    -o-border-radius: var(--border-weight);
    background-image: -webkit-gradient(linear, left bottom, left top, from(var(--border-color)), color-stop(60%, transparent));
    background-image: linear-gradient(0deg, var(--border-color) 0%, transparent 60%);
    background-size: 100% 4px;
}

.cassette-tape-container .cassette-circle-inner>i:nth-of-type(6) {
    -webkit-transform: translate3d(0px, 9.5px, 0) rotate(-180deg);
    transform: translate3d(0px, 9.5px, 0) rotate(-180deg);
}

.cassette-tape-container .cassette-circle-inner>i:nth-of-type(5) {
    -webkit-transform: translate3d(-8.22724134px, 4.75px, 0) rotate(-150deg);
    transform: translate3d(-8.22724134px, 4.75px, 0) rotate(-150deg);
}

.cassette-tape-container .cassette-circle-inner>i:nth-of-type(4) {
    -webkit-transform: translate3d(-8.22724134px, -4.75px, 0) rotate(-120deg);
    transform: translate3d(-8.22724134px, -4.75px, 0) rotate(-120deg);
}

.cassette-tape-container .cassette-circle-inner>i:nth-of-type(3) {
    -webkit-transform: translate3d(0px, -9.5px, 0) rotate(-90deg);
    transform: translate3d(0px, -9.5px, 0) rotate(-90deg);
}

.cassette-tape-container .cassette-circle-inner>i:nth-of-type(2) {
    -webkit-transform: translate3d(8.22724134px, -4.75px, 0) rotate(-60deg);
    transform: translate3d(8.22724134px, -4.75px, 0) rotate(-60deg);
}

.cassette-tape-container .cassette-circle-inner>i:nth-of-type(1) {
    -webkit-transform: translate3d(8.22724134px, 4.75px, 0) rotate(-30deg);
    transform: translate3d(8.22724134px, 4.75px, 0) rotate(-30deg);
}


/* cassette player */
.boom-box {
    position: relative;
    margin: 10px auto;
    width: calc(var(--cassette-size) * 3 + var(--border-weight) * 6);
}

.boom-box .boom-handler {
    position: absolute;
    left: 15%;
    width: 70%;
    height: 50px;
    border-width: calc(var(--border-weight) * 4) calc(var(--border-weight) * 2) 0 calc(var(--border-weight) * 2);
    border-color: var(--border-color);
    border-style: solid;
}

.boom-box .boom-player {
    position: absolute;
    top: calc(50px + var(--border-weight) * 4);
    width: 100%;
    height: auto;
    padding: 2%;
    border: calc(var(--border-weight) * 2) solid var(--border-color);
}

.boom-box .boom-nav {
    height: 100px;
    width: 100%;
    border-bottom: calc(var(--border-weight) * 2) solid var(--border-color)
}

fieldset {
    font-size: 14px;
    text-align: center;
    padding: 5px 5px;
}

.boom-player .boom-bottom {
    height: calc(var(--cassette-size) * 1.1);
    width: 100%;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: space-around;
    -ms-flex-pack: space-around;
    justify-content: space-around;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
}

.boom-bottom .boom-circle {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: space-around;
    -ms-flex-pack: space-around;
    justify-content: space-around;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    width: calc(var(--cassette-size) - 30px);
    height: calc(var(--cassette-size) - 30px);
    border: var(--border-weight) solid var(--border-color);
    border-radius: 50%;
    -webkit-border-radius: 50%;
    -moz-border-radius: 50%;
    -ms-border-radius: 50%;
    -o-border-radius: 50%;
    background-color: var(--border-color);
}

.boom-circle .boom-circle-inner {
    width: calc(var(--cassette-size) / 4);
    height: calc(var(--cassette-size) / 4);
    background-color: var(--main-color);
    border-radius: 50%;
    -webkit-border-radius: 50%;
    -moz-border-radius: 50%;
    -ms-border-radius: 50%;
    -o-border-radius: 50%;
}

.boom-bottom .cassette-box {
    margin: auto auto;
    color: var(--border-color);
    width: calc(var(--cassette-size) + var(--border-weight) * 2);
    height: calc(var(--cassette-size) / 9 * 6 + var(--border-weight) * 2);
    border: calc(var(--border-weight) * 2) solid var(--border-color);
    border-radius: var(--border-weight);
    -webkit-border-radius: var(--border-weight);
    -moz-border-radius: var(--border-weight);
    -ms-border-radius: var(--border-weight);
    -o-border-radius: var(--border-weight);
    background-color: rgba(51, 51, 51, .2);
}

.boom-bottom .cassette-box::before {
    position: absolute;
    content: "";
    display: block;
    width: calc(var(--cassette-size) + var(--border-weight) * 2);
    height: calc(var(--cassette-size) / 9 * 6 + var(--border-weight) * 2);
    background-color: rgba(51, 51, 51, .4);
    transform: translate3d(0, 0%, 0);
    z-index: 99;
    -webkit-transform: translate3d(0, 0%, 0);
    -moz-transform: translate3d(0, 0%, 0);
    -ms-transform: translate3d(0, 0%, 0);
    -o-transform: translate3d(0, 0%, 0);
}

.boom-nav {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: space-around;
    -ms-flex-pack: space-around;
    justify-content: space-around;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    width: 100%;
    height: 100%;
}

.boom-nav .boom-controls {
    width: 35%;
    height: 90px;
}

.boom-nav .boom-controls .boom-contrl-range {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: space-around;
    -ms-flex-pack: space-around;
    justify-content: space-around;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    height: 40px;
}

.boom-nav .boom-data {
    width: 60%;
    height: 80px;
    border: var(--border-weight) solid var(--border-color);
    border-radius: var(--border-weight);
    -webkit-border-radius: var(--border-weight);
    -moz-border-radius: var(--border-weight);
    -ms-border-radius: var(--border-weight);
    -o-border-radius: var(--border-weight);
}

.boom-data .audio-analyse {
    width: 100%;
    height: 80px;
}

.boom-controls input {
    display: inline-block;
    width: 90%;
}

.boom-controls label {
    display: inline-block;
    width: 10%;
}

.boom-bottom .control-play {
    background-color: var(--border-color);
    outline: none;
    border: none;
    padding: 0;
    height: 20px;
    width: var(--cassette-size);
    cursor: pointer;
}
.boom-bottom .control-play:hover {
    box-shadow: 0 1px 2px lightgray;
    height: 20px;
    width: calc(var(--cassette-size) + 4px);
}



/* data item */


/* input range
 * refer to https://codepen.io/Rumyra/pen/qyMzqN/ 
 * fix IE
*/
input[type=range] {
    -webkit-appearance: none;
    width: 80%;
    background: transparent;
}

input[type=range]::-ms-track {
    width: 100%;
    cursor: pointer;
    background: transparent;
    border-color: transparent;
    color: transparent;
}

input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    margin-top: -5px;
    height: 20px;
    width: 20px;
    border: 2px solid var(--border-color);
    border-radius: var(--border-color);
    background-color: var(--border-color);
    cursor: pointer;
    -webkit-border-radius: var(--border-color);
    -moz-border-radius: var(--border-color);
    -ms-border-radius: var(--border-color);
    -o-border-radius: var(--border-color);
}

input[type=range]::-moz-range-thumb {
    height: 15px;
    width: 20px;
    border: 2px solid var(--border-color);
    border-radius: var(--border-color);
    background-color: var(--border-color);
    cursor: pointer;
    -webkit-border-radius: var(--border-color);
    -moz-border-radius: var(--border-color);
    -ms-border-radius: var(--border-color);
    -o-border-radius: var(--border-color);
}

input[type=range]::-ms-thumb {
    height: 20px;
    width: 20px;
    border: 2px solid #333;
    border-radius: var(--border-color);
    background-color: var(--border-color);
    cursor: pointer;
    z-index: 2;
}

input[type=range]::-webkit-slider-runnable-track {
    height: 10px;
    cursor: pointer;
    background-color: var(--border-color);
    border-radius: var(--border-color);
    -webkit-border-radius: var(--border-color);
    -moz-border-radius: var(--border-color);
    -ms-border-radius: var(--border-color);
    -o-border-radius: var(--border-color);
}

input[type=range]::-moz-range-track {
    height: 15px;
    cursor: pointer;
    background-color: var(--border-color);
    border-radius: var(--border-color);
    -webkit-border-radius: var(--border-color);
    -moz-border-radius: var(--border-color);
    -ms-border-radius: var(--border-color);
    -o-border-radius: var(--border-color);
}

input[type=range]::-ms-track {
    height: 10px;
    cursor: pointer;
    background-color: #fff;
    border-radius: var(--border-color);
    -webkit-border-radius: var(--border-color);
    -moz-border-radius: var(--border-color);
    -ms-border-radius: var(--border-color);
    -o-border-radius: var(--border-color);
}

input[type=range]:focus {
    outline: none;
}

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

JS

              
                // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API

interface Params {
    playButton: string;
    volControl: string;
    panControl: string;
    visualCanvas: string;
    playList: PlayItem[];
}

interface PlayItem {
    src: string;
    type: string;
}


class AudioVisualize {
    // 声明全局变量
    private audioContext: AudioContext;
    private audioElement: HTMLAudioElement;
    private track: MediaElementAudioSourceNode;
    private playButton: HTMLButtonElement;
    private volControl: HTMLInputElement;
    private panControl: HTMLInputElement;
    private visualCanvas: HTMLCanvasElement;
    private gainNode: GainNode;
    private panner: StereoPannerNode;
    private analyser: AnalyserNode;
    private playList: PlayItem[];
    private playState: boolean;
    // 构造函数
    public constructor (params: Params) {
        if (!document || !window) {
            throw Error('document not loaded');
        }
        if (!params.playButton || !params.volControl || !params.panControl || !params.visualCanvas || !params.playList) {
            throw Error('params missing');
        }
        // initial player
        this.playState = false;
        this.playList = params.playList;
        // initial audio element 
        this.audioElement = new Audio();
        this.audioElement.preload = 'none';
        this.audioElement.loop = false;
        this.audioElement.controls = true;
        this.audioElement.crossOrigin = "anonymous";
        // initial audio context 
        this.audioContext = new AudioContext();
        this.track = this.audioContext.createMediaElementSource(this.audioElement);
        // initial dom selector
        this.playButton = document.querySelector(params.playButton);
        this.volControl = document.querySelector(params.volControl);
        this.panControl = document.querySelector(params.panControl);
        this.visualCanvas = document.querySelector(params.visualCanvas);

        if (!this.playButton || !this.volControl || !this.panControl || !this.visualCanvas) {
            throw ReferenceError('selector not valied');
        }

        this.enablePlay();
    }

    // reset play list
    public resetPlayList (playList: PlayItem[]): void {
        this.playState = false;
        this.track && this.track.disconnect();
        this.gainNode && this.gainNode.disconnect();
        this.panner && this.panner.disconnect();
        this.analyser && this.analyser.disconnect();
        this.playList = playList;
        this.audioElement.src = "";
    }
    // change current audio
    public changeAudio (index: number): boolean {
        let idx = index || 0;
        if (this.playList.length <= 0 || idx > this.playList.length) return false;
        this.playState = false;
        this.track && this.track.disconnect();
        this.gainNode && this.gainNode.disconnect();
        this.panner && this.panner.disconnect();
        this.analyser && this.analyser.disconnect();
        this.audioElement.src = this.playList[idx].src;
        this.audioElement.load();
        this._enableControls();
        return true;
    }

    private _enableControls () {
        this.enableVolume();
        this.enablePanner();
        this.enableAnalyse();
    }

    // connect to audio track
    private setTrack () {
        this.track.connect(this.gainNode)
            .connect(this.panner)
            .connect(this.analyser)
            .connect(this.audioContext.destination);
    }

    private playHandler () {
        if (this.playState === false) {
            this.audioElement.play();
            this.playState = true;
            this.playButton.dataset.playing = 'true';
        } else {
            this.audioElement.pause();
            this.playState = false;
            this.playButton.dataset.playing = 'false';
        }
    }
    // control play
    private enablePlay (): void {
        if (!this.audioElement || !this.playButton || !this.audioContext) return;
        this.audioContext.resume().then(() => {
            this.playButton.addEventListener('click', this.playHandler.bind(this), false);
            this.audioElement.addEventListener('ended', () => {
                this.playState = false;
                this.playButton.dataset.playing = 'false';
            }, false);
        }).catch(err => { console.log(err) });
    }
    // control volume
    private enableVolume (): void {
        let self = this;
        if (!this.volControl || !this.track || !this.audioContext) {
            return;
        }
        if (this.audioContext.state === 'suspended') {
            this.audioContext.resume();
        }
        this.gainNode = this.audioContext.createGain();
        this.volControl.addEventListener('input', function () {
            self.gainNode.gain.value = Number(this.value);
            self.setTrack();
        }, false);
    }
    // control panner
    private enablePanner (): void {
        let self = this;
        if (!this.volControl || !this.panControl || !this.track || !this.audioContext) {
            return;
        }
        if (this.audioContext.state === 'suspended') {
            this.audioContext.resume();
        }
        this.panner = this.audioContext.createStereoPanner();
        this.panner.pan.value = 0.0;
        this.panControl.addEventListener('input', function () {
            self.panner.pan.value = Number(this.value);
            self.setTrack();
        }, false);
    }

    // drw canvas
    private draw (analyser: AnalyserNode,
        dataArray: Uint8Array,
        canvasCtx: CanvasRenderingContext2D,
        WIDTH: number,
        HEIGHT: number,
        count: number): void {
        canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
        analyser.getByteFrequencyData(dataArray);
        let value = 0,
            step = Math.round(dataArray.length / count),
            x = 0,
            y = 0,
            lineWidth = canvasCtx.lineWidth = WIDTH / count,
            index = count;
        canvasCtx.strokeStyle = "#fff";
        while (index) {
            value = dataArray[index * step + step];
            x = index * lineWidth;
            y = HEIGHT - value * 1.5;
            canvasCtx.beginPath();
            canvasCtx.moveTo(x, HEIGHT);
            canvasCtx.lineTo(x, y);
            canvasCtx.stroke();
            index -= 2;
        }
        requestAnimationFrame(() => this.draw(analyser, dataArray, canvasCtx, WIDTH, HEIGHT, count));
    };
    // controls visualize
    public enableAnalyse (): void {
        if (!this.audioContext || !this.visualCanvas) return;
        if (this.audioContext.state === 'suspended') {
            this.audioContext.resume();
        }
        if (this.visualCanvas.getContext('2d')) {
            let canvasCtx = this.visualCanvas.getContext('2d'),
                WIDTH = this.visualCanvas.width,
                HEIGHT = this.visualCanvas.height;
            this.analyser = this.audioContext.createAnalyser();
            this.setTrack();
            let dataArray = new Uint8Array(this.analyser.frequencyBinCount);
            let count = Math.min(70, dataArray.length)
            // draw an oscilloscope of the current audio source
            this.draw(this.analyser, dataArray, canvasCtx, WIDTH, HEIGHT, count);
        }
    }
}
              
            
!
999px

Console