
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>Bedtime Tunes — tunes to snooze to</title>
  <meta name="description" content="A curated bedtime playlist running since 2006. Quiet music for falling asleep, beneath the aurora." />

  <!-- Favicon -->
  <link rel="icon" type="image/png" href="/BT.png" />
  <link rel="apple-touch-icon" href="/BT.png" />

  <!-- Open Graph -->
  <meta property="og:type" content="website" />
  <meta property="og:site_name" content="Bedtime Tunes" />
  <meta property="og:title" content="Bedtime Tunes — tunes to snooze to" />
  <meta property="og:description" content="A curated bedtime playlist running since 2006. Quiet music for falling asleep, beneath the aurora." />
  <meta property="og:url" content="https://bedtimetunes.com/" />
  <meta property="og:image" content="https://bedtimetunes.com/og-image.png" />
  <meta property="og:image:width" content="1200" />
  <meta property="og:image:height" content="630" />

  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Bedtime Tunes — tunes to snooze to" />
  <meta name="twitter:description" content="A curated bedtime playlist running since 2006. Quiet music for falling asleep, beneath the aurora." />
  <meta name="twitter:image" content="https://bedtimetunes.com/og-image.png" />

  <meta name="theme-color" content="#1a0a1e" />

  <script async src="https://www.googletagmanager.com/gtag/js?id=G-XT56GW888D"></script>
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
    gtag('config', 'G-XT56GW888D');
  </script>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link href="https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600&family=Josefin+Sans:wght@100;200;300&display=swap" rel="stylesheet" />
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    :root {
      --deep:    #1a0a1e;
      --plum:    #3d1a3a;
      --wine:    #7b2650;
      --rose:    #c2416b;
      --coral:   #e8735a;
    }
    html, body {
      margin: 0;
      min-height: 100%;
      font-family: 'Josefin Sans', sans-serif;
      background: var(--deep);
      color: #fff;
      scrollbar-width: none;
      overflow-x: hidden;
    }
    body::-webkit-scrollbar { display: none; }

    /* ── Aurora Canvas ── */
    #aurora {
      position: fixed;
      inset: 0;
      z-index: 0;
    }
    #particles {
      position: fixed;
      inset: 0;
      z-index: 2;
      pointer-events: none;
    }
    #stars {
      position: fixed;
      inset: 0;
      z-index: 1;
      pointer-events: none;
    }
    .star {
      position: absolute;
      background: #fff;
      border-radius: 50%;
      box-shadow: 0 0 4px rgba(255,255,255,0.6);
      animation: twinkle var(--d) infinite ease-in-out;
      animation-delay: var(--delay);
      opacity: var(--min-op);
    }
    @keyframes twinkle {
      0%, 100% { opacity: var(--min-op); }
      50%      { opacity: var(--max-op); }
    }

    /* Main container */
    .container {
      position: relative;
      z-index: 10;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 2rem 1.5rem;
    }

    .player-card {
      width: 100%;
      max-width: 480px;
      display: flex;
      flex-direction: column;
      gap: 1.5rem;
    }

    /* Top stats line */
    .stats-line {
      text-align: center;
      font-family: 'Barlow', sans-serif;
      font-size: 0.5rem;
      letter-spacing: 0.25em;
      text-transform: uppercase;
      color: rgba(255,255,255,0.3);
      margin-bottom: 0.4rem;
    }
    .stats-line .sep {
      color: rgba(255,255,255,0.12);
      margin: 0 0.45em;
    }

    /* Title */
    .title-row {
      text-align: center;
      margin-bottom: 0.2rem;
    }
    .title {
      font-family: 'Barlow', sans-serif;
      font-weight: 500;
      font-size: clamp(1.4rem, 3.8vw, 2rem);
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: #fff;
      margin: 0;
    }
    .tagline {
      font-family: 'Barlow', sans-serif;
      font-size: 0.55rem;
      letter-spacing: 0.25em;
      text-transform: uppercase;
      color: rgba(255,255,255,0.4);
      margin-top: 0.5rem;
    }

    /* Logo with bounce */
    .logo-wrap {
      display: flex;
      justify-content: center;
      margin: 0.5rem 0 1rem;
    }
    .logo-tooltip-wrap {
      position: relative;
      display: inline-block;
      line-height: 0;
    }
    .logo {
      width: min(190px, 42vw);
      height: min(190px, 42vw);
      border-radius: 10px;
      object-fit: cover;
      box-shadow: 0 0 80px rgba(192, 65, 107, 0.5), 0 20px 60px rgba(0,0,0,0.6);
      animation: float 8s ease-in-out infinite;
      background: linear-gradient(135deg, #c2416b, #7b2650);
    }
    @keyframes float {
      0%, 100% { transform: translateY(0) rotate(0deg); }
      50%      { transform: translateY(-10px) rotate(0.5deg); }
    }
    .logo-tooltip {
      position: absolute;
      bottom: -2.5rem;
      left: 50%;
      transform: translateX(-50%) translateY(-4px);
      background: rgba(13, 5, 16, 0.85);
      backdrop-filter: blur(12px);
      -webkit-backdrop-filter: blur(12px);
      border: 1px solid rgba(255,255,255,0.1);
      padding: 0.5rem 0.9rem;
      border-radius: 100px;
      font-family: 'Barlow', sans-serif;
      font-size: 0.55rem;
      letter-spacing: 0.2em;
      text-transform: uppercase;
      color: rgba(255,255,255,0.7);
      white-space: nowrap;
      pointer-events: none;
      opacity: 0;
      transition: opacity 0.3s ease, transform 0.3s ease;
      z-index: 50;
      line-height: 1;
    }
    .logo-tooltip-wrap:hover .logo-tooltip,
    .logo-tooltip-wrap:active .logo-tooltip {
      opacity: 1;
      transform: translateX(-50%) translateY(0);
    }

    /* Spotify embed iframe */
    .embed-card {
      background: rgba(255,255,255,0.04);
      backdrop-filter: blur(12px);
      -webkit-backdrop-filter: blur(12px);
      border: 1px solid rgba(255,255,255,0.07);
      border-radius: 16px;
      padding: 1rem;
      box-shadow: 0 10px 50px rgba(0,0,0,0.4);
    }
    .embed-card iframe {
      width: 100%;
      height: 152px;
      border: none;
      border-radius: 12px;
      display: block;
    }
    .embed-label {
      font-family: 'Barlow', sans-serif;
      font-size: 0.5rem;
      letter-spacing: 0.25em;
      text-transform: uppercase;
      color: rgba(255,255,255,0.35);
      margin-bottom: 0.7rem;
      padding: 0 0.2rem;
    }

    /* Premium upsell — second thought at the bottom */
    .premium-link {
      margin-top: 1.5rem;
      padding-top: 1.5rem;
      border-top: 1px solid rgba(255,255,255,0.06);
      text-align: center;
    }
    .premium-link p {
      font-family: 'Barlow', sans-serif;
      font-size: 0.55rem;
      letter-spacing: 0.22em;
      text-transform: uppercase;
      color: rgba(255,255,255,0.35);
      margin: 0 0 0.8rem;
    }
    .premium-link a {
      display: inline-flex;
      align-items: center;
      gap: 0.5rem;
      font-family: 'Barlow', sans-serif;
      font-size: 0.6rem;
      letter-spacing: 0.2em;
      text-transform: uppercase;
      color: rgba(255,255,255,0.65);
      text-decoration: none;
      padding: 0.6rem 1.2rem;
      border: 1px solid rgba(255,255,255,0.12);
      border-radius: 100px;
      transition: color 0.2s, border-color 0.2s, background 0.2s;
    }
    .premium-link a:hover {
      color: #fff;
      border-color: rgba(194,65,107,0.5);
      background: rgba(194,65,107,0.1);
    }

    /* Footer */
    .footer {
      width: 100%;
      max-width: 480px;
      text-align: center;
      padding: 1.5rem 0 2rem;
      font-family: 'Barlow', sans-serif;
      font-size: 0.55rem;
      letter-spacing: 0.3em;
      text-transform: uppercase;
      color: rgba(255,255,255,0.25);
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 0.5rem;
    }
    .footer a {
      color: rgba(255,255,255,0.45);
      text-decoration: none;
      border-bottom: 1px solid rgba(255,255,255,0.12);
      padding-bottom: 1px;
      transition: color 0.2s, border-color 0.2s;
    }
    .footer a:hover {
      color: rgba(255,255,255,0.85);
      border-color: rgba(255,255,255,0.3);
    }
    .footer .version {
      font-size: 0.45rem;
      letter-spacing: 0.25em;
      color: rgba(255,255,255,0.15);
    }


  </style>
</head>
<body>

<canvas id="aurora"></canvas>
<div id="stars"></div>
<canvas id="particles"></canvas>

<div class="container">
  <div class="player-card">

    <div class="stats-line">
      since 2006<span class="sep">·</span>1237 tracks<span class="sep">·</span>89 hours<span class="sep">·</span>11 contributors<span class="sep">·</span>87 saved
    </div>

    <div class="title-row">
      <h1 class="title">Bedtime Tunes</h1>
      <div class="tagline">tunes to snooze to</div>
    </div>

    <div class="logo-wrap">
      <div class="logo-tooltip-wrap">
        <img class="logo" src="./bedtimetunes.jpg" alt="Bedtime Tunes" onerror="this.style.background='linear-gradient(135deg,#c2416b,#7b2650)'" />
        <span class="logo-tooltip">for you my friend · pedro · pit <span style="color:#c2416b">&hearts;</span></span>
      </div>
    </div>

    <div class="embed-card">
      <div class="embed-label">now playing</div>
      <iframe
        src="https://open.spotify.com/embed/playlist/6AGKG8W0y4xezu8Cl6Rtkj?utm_source=generator&theme=0"
        allowfullscreen=""
        allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
        loading="lazy"></iframe>
    </div>

    <div class="premium-link">
      <p>have spotify premium?</p>
      <a href="/app.html" class="signin-btn">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
          <path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.51 17.314a.748.748 0 01-1.03.249c-2.82-1.723-6.369-2.112-10.547-1.157a.748.748 0 01-.354-1.453c4.574-1.047 8.496-.596 11.682 1.337a.748.748 0 01.249 1.024zm1.47-3.27a.936.936 0 01-1.287.308c-3.226-1.982-8.144-2.556-11.96-1.399a.937.937 0 01-.545-1.791c4.358-1.322 9.775-.681 13.484 1.594a.937.937 0 01.308 1.288zm.126-3.405c-3.868-2.297-10.248-2.508-13.939-1.387a1.124 1.124 0 01-.653-2.151c4.239-1.287 11.284-1.038 15.738 1.605a1.124 1.124 0 01-1.146 1.933z"/>
        </svg>
        sign in with spotify
      </a>
    </div>

  </div>

  <footer class="footer">
    <div>made by <a href="https://lewi.fi" target="_blank" rel="noopener">lewi.fi</a> &lt;3</div>
    <div class="version">v2.0.0 · 2026-05-29 17:30 AEST</div>
  </footer>
</div>

<script>
// ─────────────────────────────────────────────
// Aurora canvas — orbs + curtain waves + time-of-day tint
// ─────────────────────────────────────────────
(function initAurora() {
  const canvas = document.getElementById('aurora');
  const ctx    = canvas.getContext('2d');
  let W, H;

  function resize() {
    W = canvas.width  = window.innerWidth;
    H = canvas.height = window.innerHeight;
  }
  resize();

  const orbs = [
    { r:[194, 65,107], xP:0.25, yP:0.30, rW:0.65, rH:0.50, alpha:0.30, alphaA:0.12, sxA:0.18, syA:0.12, sxF:0.00009 , syF:0.00007 , sxO:0.0 , syO:1.1, aF:0.00011, aO:0.3 },
    { r:[139, 26,107], xP:0.65, yP:0.55, rW:0.70, rH:0.55, alpha:0.26, alphaA:0.10, sxA:0.20, syA:0.14, sxF:0.00008 , syF:0.00009 , sxO:2.4 , syO:0.7, aF:0.00013, aO:1.2 },
    { r:[110, 60,180], xP:0.40, yP:0.18, rW:0.60, rH:0.45, alpha:0.20, alphaA:0.12, sxA:0.22, syA:0.16, sxF:0.00010 , syF:0.00007 , sxO:4.7 , syO:3.2, aF:0.00015, aO:2.1 },
    { r:[ 60,100,200], xP:0.82, yP:0.40, rW:0.55, rH:0.42, alpha:0.18, alphaA:0.10, sxA:0.20, syA:0.13, sxF:0.00007 , syF:0.00011 , sxO:1.8 , syO:5.0, aF:0.00012, aO:0.8 },
    { r:[235,120, 70], xP:0.15, yP:0.65, rW:0.65, rH:0.50, alpha:0.32, alphaA:0.14, sxA:0.18, syA:0.14, sxF:0.00012 , syF:0.00009 , sxO:3.3 , syO:2.1, aF:0.00010, aO:3.4 },
    { r:[255,110, 40], xP:0.35, yP:0.45, rW:0.55, rH:0.42, alpha:0.55, alphaA:0.18, sxA:0.22, syA:0.16, sxF:0.00014 , syF:0.00010 , sxO:1.5 , syO:4.2, aF:0.00018, aO:2.8 },
    { r:[245,170, 80], xP:0.55, yP:0.78, rW:0.55, rH:0.42, alpha:0.24, alphaA:0.12, sxA:0.16, syA:0.18, sxF:0.00011 , syF:0.00008 , sxO:5.6 , syO:4.4, aF:0.00014, aO:1.7 },
    { r:[215, 60, 70], xP:0.75, yP:0.85, rW:0.50, rH:0.40, alpha:0.22, alphaA:0.10, sxA:0.15, syA:0.18, sxF:0.00009 , syF:0.00008 , sxO:5.0 , syO:3.7, aF:0.00014, aO:5.2 },
    { r:[255,140,100], xP:0.50, yP:0.50, rW:0.40, rH:0.32, alpha:0.16, alphaA:0.10, sxA:0.20, syA:0.16, sxF:0.00013 , syF:0.00010 , sxO:0.9 , syO:6.0, aF:0.00016, aO:4.7 },
  ];

  const curtains = [
    { color:[200, 80,130], yCenter:0.32, height:0.28, alpha:0.45, waveAmp:0.10, waveFreq:0.0028, waveSpeed: 0.00018, shimmerFreq:0.0012, shimmerAmp:0.025, shimmerSpeed: 0.00040, phase:0   },
    { color:[240,130, 80], yCenter:0.52, height:0.24, alpha:0.38, waveAmp:0.08, waveFreq:0.0024, waveSpeed:-0.00014, shimmerFreq:0.0015, shimmerAmp:0.022, shimmerSpeed:-0.00045, phase:1.7 },
    { color:[220, 90, 90], yCenter:0.70, height:0.22, alpha:0.32, waveAmp:0.07, waveFreq:0.0020, waveSpeed: 0.00012, shimmerFreq:0.0010, shimmerAmp:0.018, shimmerSpeed: 0.00035, phase:3.4 },
  ];

  function getTimeTint() {
    const h = new Date().getHours() + new Date().getMinutes() / 60;
    const anchors = [
      { h: 0,  rgb: [0.75, 0.78, 1.10] },
      { h: 4,  rgb: [0.80, 0.78, 1.05] },
      { h: 7,  rgb: [1.15, 0.95, 0.85] },
      { h: 12, rgb: [1.00, 0.95, 0.95] },
      { h: 17, rgb: [1.15, 0.90, 0.80] },
      { h: 20, rgb: [1.20, 0.85, 0.80] },
      { h: 23, rgb: [0.85, 0.80, 1.05] },
      { h: 24, rgb: [0.75, 0.78, 1.10] },
    ];
    let a, b;
    for (let i = 0; i < anchors.length - 1; i++) {
      if (h >= anchors[i].h && h <= anchors[i+1].h) { a = anchors[i]; b = anchors[i+1]; break; }
    }
    if (!a) { a = anchors[0]; b = anchors[1]; }
    const t = (h - a.h) / (b.h - a.h);
    return [
      a.rgb[0] + (b.rgb[0] - a.rgb[0]) * t,
      a.rgb[1] + (b.rgb[1] - a.rgb[1]) * t,
      a.rgb[2] + (b.rgb[2] - a.rgb[2]) * t,
    ];
  }
  let timeTint = getTimeTint();
  setInterval(() => { timeTint = getTimeTint(); }, 5 * 60 * 1000);

  function drawOrb(o, t) {
    const cx = W * (o.xP + Math.sin(t * o.sxF + o.sxO) * o.sxA);
    const cy = H * (o.yP + Math.sin(t * o.syF + o.syO) * o.syA);
    const rx = W * o.rW;
    const ry = H * o.rH;
    const r = Math.min(255, o.r[0] * timeTint[0]);
    const g = Math.min(255, o.r[1] * timeTint[1]);
    const b = Math.min(255, o.r[2] * timeTint[2]);
    const a = Math.max(0, o.alpha + Math.sin(t * o.aF + o.aO) * (o.alphaA || 0));

    ctx.save();
    ctx.translate(cx, cy);
    ctx.scale(1, ry / rx);
    const grad = ctx.createRadialGradient(0, 0, 0, 0, 0, rx);
    grad.addColorStop(0.00, `rgba(${r},${g},${b},${a.toFixed(3)})`);
    grad.addColorStop(0.35, `rgba(${r},${g},${b},${(a * 0.6).toFixed(3)})`);
    grad.addColorStop(0.65, `rgba(${r},${g},${b},${(a * 0.18).toFixed(3)})`);
    grad.addColorStop(1.00, `rgba(${r},${g},${b},0)`);
    ctx.fillStyle = grad;
    ctx.beginPath();
    ctx.arc(0, 0, rx, 0, Math.PI * 2);
    ctx.fill();
    ctx.restore();
  }

  function drawCurtain(c, t) {
    const yC      = H * c.yCenter;
    const halfH   = H * c.height * 0.5;
    const ampPx   = H * c.waveAmp;
    const shimAmp = H * c.shimmerAmp;
    const [r,g,b] = c.color;
    ctx.beginPath();
    const step = 6;
    for (let x = 0; x <= W + step; x += step) {
      const wave =
        Math.sin(x * c.waveFreq    + t * c.waveSpeed    + c.phase) * ampPx +
        Math.sin(x * c.shimmerFreq + t * c.shimmerSpeed + c.phase * 0.7) * shimAmp;
      const y = yC + wave - halfH;
      if (x === 0) ctx.moveTo(x, y);
      else         ctx.lineTo(x, y);
    }
    for (let x = W; x >= -step; x -= step) {
      const wave =
        Math.sin(x * c.waveFreq    + t * c.waveSpeed    + c.phase + 0.4) * ampPx +
        Math.sin(x * c.shimmerFreq + t * c.shimmerSpeed + c.phase * 0.7 + 0.6) * shimAmp;
      const y = yC + wave + halfH;
      ctx.lineTo(x, y);
    }
    ctx.closePath();
    const grad = ctx.createLinearGradient(0, yC - halfH * 1.5, 0, yC + halfH * 1.5);
    grad.addColorStop(0.00, `rgba(${r},${g},${b},0)`);
    grad.addColorStop(0.40, `rgba(${r},${g},${b},${c.alpha.toFixed(3)})`);
    grad.addColorStop(0.60, `rgba(${r},${g},${b},${c.alpha.toFixed(3)})`);
    grad.addColorStop(1.00, `rgba(${r},${g},${b},0)`);
    ctx.fillStyle = grad;
    ctx.fill();
  }

  let rafId;
  function frame(ts) {
    ctx.clearRect(0, 0, W, H);
    const bg = ctx.createRadialGradient(W*0.5, H*0.38, 0, W*0.5, H*0.38, H);
    bg.addColorStop(0,   '#28102e');
    bg.addColorStop(0.6, '#1a0a22');
    bg.addColorStop(1,   '#0b0410');
    ctx.fillStyle = bg;
    ctx.fillRect(0, 0, W, H);
    ctx.globalCompositeOperation = 'lighter';
    orbs.forEach(o => drawOrb(o, ts));
    curtains.forEach(c => drawCurtain(c, ts));
    ctx.globalCompositeOperation = 'source-over';
    rafId = requestAnimationFrame(frame);
  }
  rafId = requestAnimationFrame(frame);

  document.addEventListener('visibilitychange', () => {
    if (document.hidden) cancelAnimationFrame(rafId);
    else rafId = requestAnimationFrame(frame);
  });
  let resizeTimer;
  window.addEventListener('resize', () => {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(resize, 100);
  });
})();

// Stars
(function initStars() {
  const container = document.getElementById('stars');
  const count = window.innerWidth < 600 ? 80 : 180;
  for (let i = 0; i < count; i++) {
    const s = document.createElement('div');
    s.className = 'star';
    const sz = Math.random() * 2 + 0.5;
    s.style.cssText = `
      width:${sz}px; height:${sz}px;
      top:${Math.random()*100}%;
      left:${Math.random()*100}%;
      --d:${(Math.random()*4+2).toFixed(1)}s;
      --delay:${(Math.random()*5).toFixed(1)}s;
      --min-op:${(Math.random()*0.15+0.05).toFixed(2)};
      --max-op:${(Math.random()*0.5+0.3).toFixed(2)};
    `;
    container.appendChild(s);
  }
})();

// Particles
(function initParticles() {
  const canvas = document.getElementById('particles');
  const ctx = canvas.getContext('2d');
  let W, H;
  const count = window.innerWidth < 600 ? 30 : 60;
  const particles = [];

  function resize() {
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    W = canvas.width  = window.innerWidth * dpr;
    H = canvas.height = window.innerHeight * dpr;
    canvas.style.width  = window.innerWidth + 'px';
    canvas.style.height = window.innerHeight + 'px';
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  }
  let resizeTimer;
  window.addEventListener('resize', () => {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(resize, 150);
  });
  resize();

  function spawn(p, fromBottom = true) {
    p.x = Math.random() * window.innerWidth;
    p.y = fromBottom ? window.innerHeight + Math.random() * 50 : Math.random() * window.innerHeight;
    p.size = Math.random() * 1.5 + 0.4;
    p.speed = Math.random() * 0.15 + 0.05;
    p.swayAmp = Math.random() * 30 + 10;
    p.swayFreq = Math.random() * 0.001 + 0.0005;
    p.swayPhase = Math.random() * Math.PI * 2;
    p.alpha = Math.random() * 0.5 + 0.15;
    p.alphaSpeed = Math.random() * 0.0008 + 0.0003;
    p.alphaPhase = Math.random() * Math.PI * 2;
    p.baseX = p.x;
  }

  for (let i = 0; i < count; i++) {
    const p = {};
    spawn(p, false);
    particles.push(p);
  }

  let rafId;
  function frame(ts) {
    ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
    particles.forEach(p => {
      p.y -= p.speed;
      p.x = p.baseX + Math.sin(ts * p.swayFreq + p.swayPhase) * p.swayAmp;
      const a = p.alpha * (0.6 + Math.sin(ts * p.alphaSpeed + p.alphaPhase) * 0.4);
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
      ctx.fillStyle = `rgba(255, 220, 200, ${a.toFixed(3)})`;
      ctx.fill();
      if (p.y < -10) spawn(p, true);
    });
    rafId = requestAnimationFrame(frame);
  }
  rafId = requestAnimationFrame(frame);

  document.addEventListener('visibilitychange', () => {
    if (document.hidden) cancelAnimationFrame(rafId);
    else rafId = requestAnimationFrame(frame);
  });
})();
</script>

</body>
</html>
