{"product_id":"obsidian","title":"Obsidian","description":"\u003c!-- ============================================================\n     AUDIO PREVIEWS — 6 SAMPLES\n     Paste into a Custom Liquid block on your Shopify product page.\n     Replace data-audio-url values with your own file links.\n     ============================================================ --\u003e\n\n\u003c!-- Dela Gothic One font (loads once, safe to include on page) --\u003e\n\u003clink rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\"\u003e\n\u003clink rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin\u003e\n\u003clink href=\"https:\/\/fonts.googleapis.com\/css2?family=Dela+Gothic+One\u0026amp;display=swap\" rel=\"stylesheet\"\u003e\n\n\u003cstyle\u003e\n  \/* =========================================================\n     THEME VARIABLES — change these to customize colors\/fonts\n     ========================================================= *\/\n  .mwp-wrap {\n    \/* ---- COLORS ---- *\/\n    --mwp-title-color:    #1e1e1e;   \/* sample title text *\/\n    --mwp-time-color:     #6e6e6e;   \/* duration text *\/\n    --mwp-btn-color:      #3a3a3a;   \/* play\/pause icon *\/\n    --mwp-btn-hover-bg:   rgba(0,0,0,0.05);\n\n    --mwp-wave-played:    #4a4a4a;   \/* played waveform bars *\/\n    --mwp-wave-unplayed:  #c8c8c8;   \/* unplayed waveform bars *\/\n\n    --mwp-heading-color:  #1e1e1e;   \/* \"Audio Previews\" title *\/\n\n    \/* ---- FONTS ---- *\/\n    --mwp-font-body:  system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;\n    --mwp-font-head:  'Dela Gothic One', system-ui, sans-serif;\n\n    \/* ---- SIZING ---- *\/\n    --mwp-heading-size: 20px;\n    --mwp-title-size:   14px;\n    --mwp-time-size:    12px;\n  }\n\n  .mwp-wrap {\n    background: transparent;\n    font-family: var(--mwp-font-body);\n    max-width: 620px;\n    width: 100%;\n    margin: 0 auto;\n    padding: 0;\n  }\n\n  \/* ---- Master heading ---- *\/\n  .mwp-heading {\n    font-family: var(--mwp-font-head);\n    font-size: var(--mwp-heading-size);\n    color: var(--mwp-heading-color);\n    letter-spacing: 0.02em;\n    line-height: 1.2;\n    margin: 0 0 14px 0;\n    padding: 0;\n    font-weight: 400; \/* Dela Gothic One is already heavy *\/\n    text-transform: none;\n  }\n\n  .mwp-item {\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    padding: 6px 0;\n    background: transparent;\n    border: none;\n    box-shadow: none;\n    transition: opacity 0.15s ease;\n  }\n  .mwp-item + .mwp-item { margin-top: 2px; }\n\n  .mwp-play {\n    background: transparent;\n    border: none;\n    width: 32px;\n    height: 32px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n    padding: 0;\n    border-radius: 50%;\n    flex-shrink: 0;\n    color: var(--mwp-btn-color);\n    transition: background 0.15s ease;\n  }\n  .mwp-play:hover { background: var(--mwp-btn-hover-bg); }\n  .mwp-play svg { width: 20px; height: 20px; display: block; fill: currentColor; }\n\n  .mwp-col { flex: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; }\n  .mwp-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }\n\n  .mwp-title {\n    font-size: var(--mwp-title-size);\n    font-weight: 450;\n    color: var(--mwp-title-color);\n    letter-spacing: -0.01em;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    line-height: 1.3;\n  }\n  .mwp-time {\n    font-size: var(--mwp-time-size);\n    font-variant-numeric: tabular-nums;\n    color: var(--mwp-time-color);\n    flex-shrink: 0;\n    font-weight: 400;\n    letter-spacing: 0.2px;\n  }\n\n  .mwp-wave {\n    width: 100%;\n    height: 28px;\n    position: relative;\n    cursor: pointer;\n    background: transparent;\n    border-radius: 0;\n    outline: none;\n  }\n  .mwp-wave:focus-visible {\n    outline: 1px solid #b0b0b0;\n    outline-offset: 3px;\n    border-radius: 2px;\n  }\n  .mwp-wave canvas {\n    display: block;\n    width: 100%;\n    height: 28px;\n    background: transparent;\n    pointer-events: none;\n  }\n\n  @media (max-width: 400px) {\n    .mwp-wrap { --mwp-heading-size: 17px; --mwp-title-size: 13px; --mwp-time-size: 11px; }\n    .mwp-play { width: 28px; height: 28px; }\n    .mwp-play svg { width: 18px; height: 18px; }\n  }\n\u003c\/style\u003e\n\n\u003cdiv class=\"mwp-wrap\" id=\"mwpRoot\"\u003e\n\n  \u003ch2 class=\"mwp-heading\"\u003eAudio Previews\u003c\/h2\u003e\n\n  \u003c!-- ============ SAMPLE 1 ============ --\u003e\n  \u003cdiv class=\"mwp-item\" data-audio-url=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0694\/5387\/3372\/files\/OB_Swan_88_BPM_D_min.wav?v=17926387293\"\u003e\n    \u003cbutton class=\"mwp-play\" aria-label=\"Play sample\"\u003e\n      \u003csvg class=\"mwp-icon-play\" viewbox=\"0 0 24 24\"\u003e\u003cpolygon points=\"8,5 19,12 8,19\"\u003e\u003c\/polygon\u003e\u003c\/svg\u003e\n      \u003csvg class=\"mwp-icon-pause\" viewbox=\"0 0 24 24\" style=\"display:none;\"\u003e\u003crect x=\"6\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003crect x=\"14\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003c\/svg\u003e\n    \u003c\/button\u003e\n    \u003cdiv class=\"mwp-col\"\u003e\n      \u003cdiv class=\"mwp-row\"\u003e\n        \u003cspan class=\"mwp-title\"\u003eSwan 88 BPM Dmin\u003c\/span\u003e\n        \u003cspan class=\"mwp-time\"\u003e0:00\u003c\/span\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"mwp-wave\" tabindex=\"0\" role=\"slider\" aria-label=\"waveform progress\"\u003e\n        \u003ccanvas width=\"600\" height=\"28\"\u003e\u003c\/canvas\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- ============ SAMPLE 2 ============ --\u003e\n  \u003cdiv class=\"mwp-item\" data-audio-url=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0694\/5387\/3372\/files\/OB_Ronin_89_BPM_Emin.wav?v=1792638729\"\u003e\n    \u003cbutton class=\"mwp-play\" aria-label=\"Play sample\"\u003e\n      \u003csvg class=\"mwp-icon-play\" viewbox=\"0 0 24 24\"\u003e\u003cpolygon points=\"8,5 19,12 8,19\"\u003e\u003c\/polygon\u003e\u003c\/svg\u003e\n      \u003csvg class=\"mwp-icon-pause\" viewbox=\"0 0 24 24\" style=\"display:none;\"\u003e\u003crect x=\"6\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003crect x=\"14\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003c\/svg\u003e\n    \u003c\/button\u003e\n    \u003cdiv class=\"mwp-col\"\u003e\n      \u003cdiv class=\"mwp-row\"\u003e\n        \u003cspan class=\"mwp-title\"\u003eRonin 89 BPM Emin\u003c\/span\u003e\n        \u003cspan class=\"mwp-time\"\u003e0:00\u003c\/span\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"mwp-wave\" tabindex=\"0\" role=\"slider\" aria-label=\"waveform progress\"\u003e\n        \u003ccanvas width=\"600\" height=\"28\"\u003e\u003c\/canvas\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- ============ SAMPLE 3 ============ --\u003e\n  \u003cdiv class=\"mwp-item\" data-audio-url=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0694\/5387\/3372\/files\/OB_No_Witnesses_88_BPM_Cmin.wav?v=1792638729\"\u003e\n    \u003cbutton class=\"mwp-play\" aria-label=\"Play sample\"\u003e\n      \u003csvg class=\"mwp-icon-play\" viewbox=\"0 0 24 24\"\u003e\u003cpolygon points=\"8,5 19,12 8,19\"\u003e\u003c\/polygon\u003e\u003c\/svg\u003e\n      \u003csvg class=\"mwp-icon-pause\" viewbox=\"0 0 24 24\" style=\"display:none;\"\u003e\u003crect x=\"6\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003crect x=\"14\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003c\/svg\u003e\n    \u003c\/button\u003e\n    \u003cdiv class=\"mwp-col\"\u003e\n      \u003cdiv class=\"mwp-row\"\u003e\n        \u003cspan class=\"mwp-title\"\u003eNo Witnesses 88 BPM Cmin\u003c\/span\u003e\n        \u003cspan class=\"mwp-time\"\u003e0:00\u003c\/span\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"mwp-wave\" tabindex=\"0\" role=\"slider\" aria-label=\"waveform progress\"\u003e\n        \u003ccanvas width=\"600\" height=\"28\"\u003e\u003c\/canvas\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- ============ SAMPLE 4 ============ --\u003e\n  \u003cdiv class=\"mwp-item\" data-audio-url=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0694\/5387\/3372\/files\/OB_Rogue_Code_82_BPM_Amin.wav?v=1792638729\"\u003e\n    \u003cbutton class=\"mwp-play\" aria-label=\"Play sample\"\u003e\n      \u003csvg class=\"mwp-icon-play\" viewbox=\"0 0 24 24\"\u003e\u003cpolygon points=\"8,5 19,12 8,19\"\u003e\u003c\/polygon\u003e\u003c\/svg\u003e\n      \u003csvg class=\"mwp-icon-pause\" viewbox=\"0 0 24 24\" style=\"display:none;\"\u003e\u003crect x=\"6\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003crect x=\"14\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003c\/svg\u003e\n    \u003c\/button\u003e\n    \u003cdiv class=\"mwp-col\"\u003e\n      \u003cdiv class=\"mwp-row\"\u003e\n        \u003cspan class=\"mwp-title\"\u003eRogue Code 82 BPM Amin\u003c\/span\u003e\n        \u003cspan class=\"mwp-time\"\u003e0:00\u003c\/span\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"mwp-wave\" tabindex=\"0\" role=\"slider\" aria-label=\"waveform progress\"\u003e\n        \u003ccanvas width=\"600\" height=\"28\"\u003e\u003c\/canvas\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- ============ SAMPLE 5 ============ --\u003e\n  \u003cdiv class=\"mwp-item\" data-audio-url=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0694\/5387\/3372\/files\/OB_High_Stakes_89_BPM_Emin.wav?v=1792638729\"\u003e\n    \u003cbutton class=\"mwp-play\" aria-label=\"Play sample\"\u003e\n      \u003csvg class=\"mwp-icon-play\" viewbox=\"0 0 24 24\"\u003e\u003cpolygon points=\"8,5 19,12 8,19\"\u003e\u003c\/polygon\u003e\u003c\/svg\u003e\n      \u003csvg class=\"mwp-icon-pause\" viewbox=\"0 0 24 24\" style=\"display:none;\"\u003e\u003crect x=\"6\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003crect x=\"14\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003c\/svg\u003e\n    \u003c\/button\u003e\n    \u003cdiv class=\"mwp-col\"\u003e\n      \u003cdiv class=\"mwp-row\"\u003e\n        \u003cspan class=\"mwp-title\"\u003eHigh Stakes 89 BPM Emin\u003c\/span\u003e\n        \u003cspan class=\"mwp-time\"\u003e0:00\u003c\/span\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"mwp-wave\" tabindex=\"0\" role=\"slider\" aria-label=\"waveform progress\"\u003e\n        \u003ccanvas width=\"600\" height=\"28\"\u003e\u003c\/canvas\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- ============ SAMPLE 6 ============ --\u003e\n  \u003cdiv class=\"mwp-item\" data-audio-url=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0694\/5387\/3372\/files\/OB_Gone_Girl_86_BPM_D_min.wav?v=1792638729\"\u003e\n    \u003cbutton class=\"mwp-play\" aria-label=\"Play sample\"\u003e\n      \u003csvg class=\"mwp-icon-play\" viewbox=\"0 0 24 24\"\u003e\u003cpolygon points=\"8,5 19,12 8,19\"\u003e\u003c\/polygon\u003e\u003c\/svg\u003e\n      \u003csvg class=\"mwp-icon-pause\" viewbox=\"0 0 24 24\" style=\"display:none;\"\u003e\u003crect x=\"6\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003crect x=\"14\" y=\"4\" width=\"4\" height=\"16\"\u003e\u003c\/rect\u003e\u003c\/svg\u003e\n    \u003c\/button\u003e\n    \u003cdiv class=\"mwp-col\"\u003e\n      \u003cdiv class=\"mwp-row\"\u003e\n        \u003cspan class=\"mwp-title\"\u003eGone Girl 86 BPM Dmin\u003c\/span\u003e\n        \u003cspan class=\"mwp-time\"\u003e0:00\u003c\/span\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"mwp-wave\" tabindex=\"0\" role=\"slider\" aria-label=\"waveform progress\"\u003e\n        \u003ccanvas width=\"600\" height=\"28\"\u003e\u003c\/canvas\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/div\u003e\n\n\u003c\/div\u003e\n\n\u003cscript\u003e\n(function () {\n  \"use strict\";\n\n  if (!CanvasRenderingContext2D.prototype.roundRect) {\n    CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {\n      if (w \u003c 2 * r) r = w \/ 2;\n      if (h \u003c 2 * r) r = h \/ 2;\n      this.moveTo(x + r, y);\n      this.lineTo(x + w - r, y);\n      this.quadraticCurveTo(x + w, y, x + w, y + r);\n      this.lineTo(x + w, y + h - r);\n      this.quadraticCurveTo(x + w, y + h, x + w - r, y + h);\n      this.lineTo(x + r, y + h);\n      this.quadraticCurveTo(x, y + h, x, y + h - r);\n      this.lineTo(x, y + r);\n      this.quadraticCurveTo(x, y, x + r, y);\n      return this;\n    };\n  }\n\n  function formatTime(seconds) {\n    if (!isFinite(seconds) || seconds \u003c 0) return \"0:00\";\n    var m = Math.floor(seconds \/ 60);\n    var s = Math.floor(seconds % 60);\n    return m + \":\" + (s \u003c 10 ? \"0\" : \"\") + s;\n  }\n\n  function generateTamuzWaveform(seed, count) {\n    var data = [];\n    function rand() {\n      seed = (seed * 9301 + 49297) % 233280;\n      return seed \/ 233280;\n    }\n    for (var i = 0; i \u003c count; i++) {\n      var t = i \/ (count - 1);\n      var edge = 1;\n      var taperLen = 0.06;\n      if (t \u003c taperLen) edge = t \/ taperLen;\n      else if (t \u003e 1 - taperLen) edge = (1 - t) \/ taperLen;\n      edge = edge * edge * (3 - 2 * edge);\n\n      var wave1 = Math.sin(t * Math.PI * 6) * 0.15;\n      var wave2 = Math.sin(t * Math.PI * 14 + 1.3) * 0.10;\n      var wave3 = Math.sin(t * Math.PI * 23 + 2.7) * 0.07;\n      var spike = 0;\n      if (rand() \u003e 0.72) spike = rand() * 0.35;\n\n      var base = 0.42 + wave1 + wave2 + wave3 + spike;\n      var asymmetry = 0.02 * Math.sin(t * Math.PI * 3);\n      var amp = base + asymmetry;\n      amp = Math.max(0.08, Math.min(0.98, amp));\n      amp = amp * edge;\n      data.push(amp);\n    }\n    return data;\n  }\n\n  function createPlayer(item, index, rootEl) {\n    var audio = new Audio(item.dataset.audioUrl);\n    audio.preload = \"metadata\";\n\n    var playBtn = item.querySelector(\".mwp-play\");\n    var playIcon = item.querySelector(\".mwp-icon-play\");\n    var pauseIcon = item.querySelector(\".mwp-icon-pause\");\n    var timeEl = item.querySelector(\".mwp-time\");\n    var waveEl = item.querySelector(\".mwp-wave\");\n    var canvas = item.querySelector(\"canvas\");\n    var ctx = canvas.getContext(\"2d\");\n\n    var isPlaying = false;\n    var animationFrame = null;\n    var duration = 0;\n    var barCount = 90;\n    var waveform = generateTamuzWaveform(index * 47 + 13, barCount);\n\n    \/\/ Read CSS variable colors at draw time so user can override via CSS\n    function getColors() {\n      var cs = getComputedStyle(rootEl);\n      return {\n        played:   (cs.getPropertyValue(\"--mwp-wave-played\")   || \"#4a4a4a\").trim() || \"#4a4a4a\",\n        unplayed: (cs.getPropertyValue(\"--mwp-wave-unplayed\") || \"#c8c8c8\").trim() || \"#c8c8c8\"\n      };\n    }\n\n    function draw(progress) {\n      var dpr = window.devicePixelRatio || 1;\n      var cssW = waveEl.clientWidth;\n      var cssH = 28;\n      if (cssW \u003c= 0) return;\n\n      canvas.width = cssW * dpr;\n      canvas.height = cssH * dpr;\n      canvas.style.width = cssW + \"px\";\n      canvas.style.height = cssH + \"px\";\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n      ctx.clearRect(0, 0, cssW, cssH);\n\n      var colors = getColors();\n      var gap = 1;\n      var barW = (cssW - (barCount - 1) * gap) \/ barCount;\n      if (barW \u003c 0.6) {\n        gap = 0.6;\n        barW = Math.max(0.4, (cssW - (barCount - 1) * gap) \/ barCount);\n      }\n\n      var midY = cssH \/ 2;\n      var maxH = cssH * 0.85;\n      var playedRatio = Math.min(1, Math.max(0, progress));\n      var playedBoundary = playedRatio * cssW;\n\n      for (var i = 0; i \u003c barCount; i++) {\n        var x = i * (barW + gap);\n        var amp = waveform[i] || 0.2;\n        var h = Math.max(2, amp * maxH);\n        var y = midY - h \/ 2;\n        var center = x + barW \/ 2;\n        ctx.fillStyle = (center \u003c= playedBoundary) ? colors.played : colors.unplayed;\n        var r = Math.min(1, barW \/ 2);\n        ctx.beginPath();\n        ctx.roundRect(x, y, barW, h, r);\n        ctx.fill();\n      }\n    }\n\n    function loop() {\n      if (audio.duration \u0026\u0026 isFinite(audio.duration) \u0026\u0026 audio.duration \u003e 0) {\n        draw(audio.currentTime \/ audio.duration);\n      }\n      if (isPlaying) animationFrame = requestAnimationFrame(loop);\n    }\n    function startLoop() { if (animationFrame) cancelAnimationFrame(animationFrame); animationFrame = requestAnimationFrame(loop); }\n    function stopLoop() { if (animationFrame) { cancelAnimationFrame(animationFrame); animationFrame = null; } }\n\n    function setPlaying(playing) {\n      isPlaying = playing;\n      playIcon.style.display = playing ? \"none\" : \"block\";\n      pauseIcon.style.display = playing ? \"block\" : \"none\";\n      playBtn.setAttribute(\"aria-label\", playing ? \"Pause sample\" : \"Play sample\");\n      if (playing) startLoop();\n      else {\n        stopLoop();\n        if (audio.duration \u0026\u0026 isFinite(audio.duration) \u0026\u0026 audio.duration \u003e 0) {\n          draw(audio.currentTime \/ audio.duration);\n        }\n      }\n    }\n\n    function toggle() {\n      if (isPlaying) { audio.pause(); }\n      else {\n        if (window.__mwpPlayers) {\n          window.__mwpPlayers.forEach(function (p) {\n            if (p !== api \u0026\u0026 !p.audio.paused) p.audio.pause();\n          });\n        }\n        if (audio.ended) audio.currentTime = 0;\n        audio.play().catch(function (e) { console.warn(\"playback failed:\", e); });\n      }\n    }\n\n    function seek(e) {\n      if (!audio.duration || !isFinite(audio.duration)) return;\n      var rect = waveEl.getBoundingClientRect();\n      var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;\n      var ratio = Math.min(1, Math.max(0, x \/ rect.width));\n      audio.currentTime = ratio * audio.duration;\n      draw(ratio);\n    }\n\n    audio.addEventListener(\"loadedmetadata\", function () {\n      duration = audio.duration;\n      timeEl.textContent = formatTime(duration);\n      draw(0);\n    });\n    audio.addEventListener(\"play\", function () { setPlaying(true); });\n    audio.addEventListener(\"pause\", function () { setPlaying(false); });\n    audio.addEventListener(\"ended\", function () {\n      setPlaying(false);\n      audio.currentTime = 0;\n      draw(0);\n    });\n    audio.addEventListener(\"timeupdate\", function () {\n      if (!isPlaying \u0026\u0026 audio.duration \u0026\u0026 isFinite(audio.duration)) {\n        draw(audio.currentTime \/ audio.duration);\n      }\n      if (duration === 0 \u0026\u0026 audio.duration \u0026\u0026 isFinite(audio.duration)) {\n        duration = audio.duration;\n        timeEl.textContent = formatTime(duration);\n      }\n    });\n\n    playBtn.addEventListener(\"click\", toggle);\n    waveEl.addEventListener(\"click\", seek);\n    waveEl.addEventListener(\"touchstart\", function (e) { e.preventDefault(); seek(e); }, { passive: false });\n    waveEl.addEventListener(\"keydown\", function (e) {\n      if (e.key === \"ArrowLeft\" || e.key === \"ArrowRight\") {\n        e.preventDefault();\n        if (!audio.duration || !isFinite(audio.duration)) return;\n        var step = 0.05 * (e.key === \"ArrowLeft\" ? -1 : 1);\n        var nt = Math.max(0, Math.min(audio.duration, audio.currentTime + step * audio.duration));\n        audio.currentTime = nt;\n        draw(nt \/ audio.duration);\n      }\n    });\n\n    var resizeTimer = null;\n    window.addEventListener(\"resize\", function () {\n      if (resizeTimer) clearTimeout(resizeTimer);\n      resizeTimer = setTimeout(function () {\n        if (audio.duration \u0026\u0026 isFinite(audio.duration)) draw(audio.currentTime \/ audio.duration);\n        else draw(0);\n      }, 100);\n    });\n\n    setTimeout(function () { draw(0); }, 50);\n\n    var api = { audio: audio, draw: draw, getPlaying: function () { return isPlaying; } };\n    return api;\n  }\n\n  function boot() {\n    var root = document.getElementById(\"mwpRoot\");\n    if (!root) return;\n    var items = root.querySelectorAll(\".mwp-item\");\n    if (!items.length) return;\n    window.__mwpPlayers = [];\n    items.forEach(function (item, idx) {\n      window.__mwpPlayers.push(createPlayer(item, idx, root));\n    });\n  }\n\n  if (document.readyState === \"loading\") document.addEventListener(\"DOMContentLoaded\", boot);\n  else boot();\n})();\n\u003c\/script\u003e\n\u003cp class=\"p1\"\u003eBuilt for boom bap producers looking for dark melodies with that gritty, hard-hitting feel. This collection features \u003cstrong\u003e40 dark samples\u003c\/strong\u003e inspired by the raw sound of 90s hip hop and the darker side of modern boom bap. Expect haunting keys, eerie textures, and moody compositions that leave plenty of room for hard drums.\u003c\/p\u003e\n\u003cp class=\"p1\"\u003eEach sample comes with its own stems, giving you the freedom to chop, rearrange, and flip the melodies to fit your sound.\u003c\/p\u003e\n\u003cp class=\"p3\"\u003e\u003cb\u003ePack Contains:\u003c\/b\u003e\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003e40 Samples\u003c\/li\u003e\n\u003cli\u003eIndividual Stems for Flexible Chopping and Rearranging\u003c\/li\u003e\n\u003cli\u003e108 Total Audio Files\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003eSize: 740 MB\u003c\/p\u003e\n\u003cp\u003e\u003cmeta charset=\"utf-8\"\u003e24-bit \/ 44.1 kHz\u003c\/p\u003e\n\u003cp\u003e \u003c\/p\u003e\n\u003cp\u003e\u003cmeta charset=\"utf-8\"\u003e\u003cspan\u003eAll samples are BPM and key labeled.\u003c\/span\u003e\u003c\/p\u003e","brand":"Analog Sound","offers":[{"title":"Default Title","offer_id":67860110213340,"sku":null,"price":29.99,"currency_code":"USD","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0694\/5387\/3372\/files\/ObsidianCover.png?v=1792635881","url":"https:\/\/www.analogsoundkits.com\/da\/products\/obsidian","provider":"Analog Sound","version":"1.0","type":"link"}