webgl+canvas浏览器中自动生成音乐可视化效果代码

代码语言:html

所属分类:多媒体

代码描述:webgl+canvas浏览器中自动生成音乐可视化效果代码,使用webkitAudioContext来弹奏生成播放音乐。

代码标签: webgl canvas 浏览器 自动 生成 音乐 可视化

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  


  <meta name="viewport" content="width=device-width, initial-scale=1">
  
  
  
<style>
:root {
  --clr-fg: #fff7;
  --clr-active-fg: #fff9;
}

html,
body {
  margin: 0;
  padding: 0;
}

canvas {
  position: fixed;
  z-index: -2;
  width: 100%;
  height: auto;
  inset: 0;
  user-select: none;
}

.control {
  display: grid;
  place-content: center;
  position: absolute;
  width: 200px;
  height: 200px;
  inset: 0;
  margin: auto;
}

.control_label {
  opacity: 0;
  display: grid;
  place-content: center;
  transition: opacity 1200ms cubic-bezier(0.075, 0.82, 0.165, 1);
}

.control.initial .control_label {
  opacity: 1;
}

.control:hover .control_label {
  opacity: 1;
}

.control_display,
.control_input {
  grid-row: 1/-1;
  grid-column: 1/-1;
  position: relative;
  cursor: pointer;
}

.control_display {
  display: grid;
  place-content: center;
  font-size: 0.825em;
  font-weight: 500;
}

.control_display svg {
  --duration: 50ms;
  display: inline-block;
  stroke-linejoin: round;
  stroke-width: 2px;
  fill: var(--clr-fg);
  transition: var(--duration);
  transform: scale(1.1);
}

.control_display svg .speaker-vol {
  stroke-linecap: round;
}

.control_display svg .speaker-on {
  fill: none;
  stroke: none;
  opacity: 0;
  transition: opacity var(--duration);
}

.control_label:has([type="checkbox"]:checked) svg {
  fill: var(--clr-active-fg);
}

.control_label:has([type="checkbox"]:checked) svg .speaker-on {
  opacity: 1;
  fill: var(--clr-active-fg);
}

input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  background-color: transparent;
  position: relative;
  margin: 0;
  width: 6em;
  height: 6em;
  transform: scale(1);
  font: inherit;
  color: currentColor;
  border-width: 0.15rem;
  border-style: solid;
  border-radius: 50%;
  border-color: var(--clr-fg);
  display: grid;
  place-content: center;
  background: #11111199;
  box-shadow: 0px 0px 12px 0 #a5a5a5;
}

input[type="checkbox"]:hover {
  transform: scale(1.1);
  transition: 300ms cubic-bezier(0.075, 0.82, 0.165, 1);
}

input[type="checkbox"]:is(:checked, :active),
input[type="checkbox"]:is(:checked, :active) + .control_display svg {
  transform: scale(1);
}

input[type="checkbox"]::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 2px 5px 2px #fff9, 0 2px 10px 10px #fff7, 0 2px 20px 20px #fff5;
  opacity: 0;
  transform: scale(0.95);
  transition: 400ms;
}

input[type="checkbox"]:hover::after {
  transform: scale(1.05);
  opacity: 1;
}
</style>

  
  
  
</head>

<body translate="no">
  <canvas id="canvas"></canvas>
<div class="control initial">
  <label class="control_label">
    <input type="checkbox" class="control_input" name="vol" id="play">
    <div class="control_display">
      <svg viewBox="0 0 60 60" width="60" height="60">
        <path class="speaker-cone" d="
            M5 25
            Q10 25
            30 5
            L30 55
            Q10 35
            5 35
            Z 
            "></path>
        <path class="speaker-vol" d="
            M35 25
            A 5 5 0 0 1 35 35
            "></path>
        <path class="speaker-vol speaker-on" d="
            M35 15
            A 5 5 0 0 1 35 45
            "></path>
      </svg>
    </div>
  </label>
</div>
  
      <script >
const choose = a => a[Math.floor(Math.random() * a.length)];
const speeds = [.3, .45];
const resont = [.25, .5];
const volmns = [.8, .9, 1];
const options = {
  volume: choose(volmns),
  resonance: choose(resont),
  speed: choose(speeds),
  rand: 1 };


let nextSpeed = options.speed;
let nextVolume = .2;

const Composer = () => {
  /******************************
   * Original Author: Yota Morimoto
   * Web Site: https://yota.tehis.net/asg/
   */

  let context, main, verb, bus, compressor;

  const trigger = Object.freeze({
    trig: function () {
      let prev = 0;
      let t = false;
      return function (val) {
        t = prev < 0 && val >= 0;
        prev = val;
        return t;
      };
    },
    change: function () {
      let prev = 0;
      let t = false;
      return function (val) {
        t = prev != val;
        prev = val;
        return t;
      };
    },
    pulse: function (threshold) {
      let prev = 0;
      let t = false;
      return function (b) {
        if (b) prev++;
        if (prev == threshold) {
          t = true;
          prev = 0;
        } else {t = false;}
        return t;
      };
    } });


  const roots = [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31];
  const melodicminor = [0, 2, 3, 5, 7, 9, 11];
  const locrian = [0, 1, 3, 5, 6, 8, 10];
  const phrygian = [0, 1, 3, 5, 7, 8, 10];
  const aeolian = [0, 2, 3, 5, 7, 8, 10];
  const dorian = [0, 2, 3, 5, 7, 9, 10];
  const ionian = [0, 2, 4, 5, 7, 9, 11];
  const mixolydian = [0, 2, 4, 5, 7, 9, 10];
  const lydian = [0, 2, 4, 6, 7, 9, 11];
  const mode = [melodicminor, locrian, phrygian, aeolian, dorian, ionian, mixolydian, lydian];
  const lowTrig = trigger.trig();
  const highTrig = trigger.trig();
  const lowChanged = trigger.change();
  const highChanged = trigger.change();
  const pulseTrig = trigger.trig();
  const pulsed = trigger.pulse(20);

  let b = Float32Array.from({ length: 3 }, () => Math.random() * 2 - 1);
  let m = pickItem(b[0], 0, 20, mode);
  let root = pickItem(b[1], 0, 20, roots);
  let then = 0,
  speedAnimStart = 0,
  volAnimStart = 0,
  volAnimStarted = false,
  speedAnimStarted = false,
  animationDuration = 5000;

  options.rand = lerp(b[2], -1, 1, 0.05, 1);

  function loop(now) {
    if (now - then > expInterp(nextSpeed, 0, 1, 800, 80)) {
      compose();
      then = now;
    }
    if (Math.abs(options.speed - nextSpeed) > .001) {
      if (!speedAnimStarted) {
        speedAnimStart = now;
        speedAnimStarted = true;
      }
      const elapsed = now - speedAnimStart;
      const fraction = elapsed / animationDuration;
      if (fraction < 1) {
        const val = fractInterp(nextSpeed, options.speed, fraction);
        nextSpeed = round(1e3 * val) * 1e-3;
      } else {
        speedAnimStarted = false;
      }
    }
    if (Math.abs(options.volume - nextVolume) > .001) {
      if (!volAnimStarted) {
        volAnimStart = now;
        volAnimStarted = true;
      }
      const elapsed = now - volAnimStart;
      const fraction = elapsed / (animationDuration * 20);
      if (fraction < 1) {
        const val = fractInterp(nextVolume, options.volume, fraction);
        nextVolume = round(1e3 * val) * 1e-3;
        main.gain.value = dbamp(lerp(nextVolume, 0, 1, -60, 3));
      } else {
        volAnimStarted = false;
      }
    }
    requestAnimationFrame(loop);
  }

  function compose() {
    b = Float32Array.from({ length: 24 }, () => Math.random() * 2 - 1);

    if (pulsed(pulseTrig(b[0]))) {
      m = pickItem(b[0], 0, 20, mode);
      root = pickItem(b[1], 0, 20, roots);
      options.speed = choose(speeds);

      console.table(options);
    }

    if (lowTrig(b[10])) {
      const note = pickNote(b[11], 15, 35);
      if (lowChanged(note)) {
        sine(
        midicps(note),
        pickVolume(b[12], -24, -9),
        b[13]);

      }
    }

    if (highTrig(b[20])) {
      const note = pickNote(b[21], 20, 40);
      if (highChanged(note)) {
        sine(
        midicps(note),
        pickVolume(b[22], -24, -9),
        b[23]);

      }
    }
  }

  function pickItem(v, a, b, array) {
    return array[round(lerp(v, -1, 1, a, b)) % (array.length - 1)];
  .........完整代码请登录后点击上方下载按钮下载查看

网友评论0