{"id":1851,"date":"2026-03-14T12:04:27","date_gmt":"2026-03-14T16:04:27","guid":{"rendered":"https:\/\/zacharybaiel.com\/blog\/?p=1851"},"modified":"2026-03-16T11:23:46","modified_gmt":"2026-03-16T15:23:46","slug":"wfmu-fast-foward-and-rewind-buttons-for-the-archive-player","status":"publish","type":"post","link":"https:\/\/zacharybaiel.com\/blog\/2026\/03\/14\/wfmu-fast-foward-and-rewind-buttons-for-the-archive-player\/","title":{"rendered":"WFMU Fast-Foward and Rewind Buttons for the Archive Player"},"content":{"rendered":"\n<p>I used ChatGPT to create this UserScript when someone was asking for a fast-forward and rewind feature in WFMU&#8217;s archive player. I have often wanted something like for myself, too.<\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;\/\/ ==UserScript==\n\/\/ @name         WFMU AccuPlayer \u00b110s\n\/\/ @namespace    local\n\/\/ @version      1.1\n\/\/ @description  Add 10-second rewind and fast-forward buttons to WFMU AccuPlayer\n\/\/ @match        *:\/\/*.wfmu.org\/archiveplayer\/*\n\/\/ @grant        none\n\/\/ @lastUpdated  2026-03-14\n\/\/ ==\/UserScript==\n\n(() =&gt; {\n  &#039;use strict&#039;;\n\n  const STEP = 10;\n  const BTN_CLASS = &#039;wfmu-skip-button&#039;;\n\n  const jump = (player, delta) =&gt; {\n    if (!player || !Number.isFinite(player.duration)) return;\n    player.currentTime = Math.max(0, Math.min(player.currentTime + delta, player.duration));\n  };\n\n  const makeButton = (label, title, onClick) =&gt; {\n    const btn = document.createElement(&#039;button&#039;);\n    btn.type = &#039;button&#039;;\n    btn.className = BTN_CLASS;\n    btn.textContent = label;\n    btn.title = title;\n    btn.addEventListener(&#039;click&#039;, onClick);\n    return btn;\n  };\n\n  const isEditable = (el) =&gt;\n    el instanceof HTMLInputElement ||\n    el instanceof HTMLTextAreaElement ||\n    el?.isContentEditable;\n\n  const addStyles = () =&gt; {\n    if (document.getElementById(&#039;wfmu-skip-styles&#039;)) return;\n\n    const style = document.createElement(&#039;style&#039;);\n    style.id = &#039;wfmu-skip-styles&#039;;\n    style.textContent = `\n      .${BTN_CLASS} {\n        min-width: 44px;\n        height: 44px;\n        padding: 0 10px;\n        border: 1px solid #000;\n        border-radius: 999px;\n        background: #000;\n        color: #fff;\n        cursor: pointer;\n        font: 600 14px\/1 sans-serif;\n      }\n    `;\n    document.head.append(style);\n  };\n\n  const init = () =&gt; {\n    const player = document.querySelector(&#039;#audio-player&#039;);\n    const transport = document.querySelector(&#039;.transport&#039;);\n    const playButton = document.querySelector(&#039;#play-button&#039;);\n\n    if (!player || !transport || !playButton) return false;\n    if (document.querySelector(`.${BTN_CLASS}`)) return true;\n\n    addStyles();\n\n    playButton.before(\n      makeButton(&#039;\u00ab 10&#039;, &#039;Back 10 seconds&#039;, () =&gt; jump(player, -STEP))\n    );\n    playButton.after(\n      makeButton(&#039;10 \u00bb&#039;, &#039;Forward 10 seconds&#039;, () =&gt; jump(player, STEP))\n    );\n\n    Object.assign(transport.style, {\n      display: &#039;flex&#039;,\n      alignItems: &#039;center&#039;,\n      gap: &#039;10px&#039;,\n    });\n\n    document.addEventListener(&#039;keydown&#039;, (e) =&gt; {\n      if (isEditable(e.target)) return;\n      if (e.key === &#039;ArrowLeft&#039; || e.key === &#039;ArrowRight&#039;) {\n        jump(player, e.key === &#039;ArrowLeft&#039; ? -STEP : STEP);\n      }\n    });\n\n    return true;\n  };\n\n  if (init()) return;\n\n  const observer = new MutationObserver(() =&gt; {\n    if (init()) observer.disconnect();\n  });\n\n  observer.observe(document.documentElement, { childList: true, subtree: true });\n})();&lt;\/code&gt;<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I used ChatGPT to create this UserScript when someone was asking for a fast-forward and rewind feature in WFMU&#8217;s archive player. I have often wanted&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[30,37],"tags":[],"class_list":["post-1851","post","type-post","status-publish","format-standard","hentry","category-participatory-culture","category-scripting-applications","entry"],"_links":{"self":[{"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/posts\/1851","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/comments?post=1851"}],"version-history":[{"count":3,"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/posts\/1851\/revisions"}],"predecessor-version":[{"id":1858,"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/posts\/1851\/revisions\/1858"}],"wp:attachment":[{"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/media?parent=1851"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/categories?post=1851"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zacharybaiel.com\/blog\/wp-json\/wp\/v2\/tags?post=1851"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}