site 1.4.0: Pionaarr Mods -- plainer text, player look, moving background, stock switch

- Hero renamed Pionaarr Mods with a short plain explanation; card texts trimmed.
- New before/after shots: stock against every mod incl. those in testing (3-band waveform).
- Look taken from the player: LCD chip, CDJ-style tabs, Chakra Petch/Barlow, a record
  and a 3-band waveform running in the background (static with reduced motion).
- Builder: Stock firmware switch clears and locks the mods, gives the picks back when off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Thiemo TizianiandClaude Opus 5.5 committed 2026-10-20 10:21:53 +02:00
1 parent e6351de711
commit be45158e3f
11 files changed
+252 -62

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 40 KiB

+10 -1
View File
@@ -1,8 +1,17 @@
// Generated by tools/make_builder_data.py from tools/changelog.json and the mods' versions.
window.CDJ_VERSIONS = {
"site": "1.3.0",
"site": "1.4.0",
"date": "2026-10-20",
"log": [
{
"version": "1.4.0",
"date": "2026-10-20",
"changes": [
"The site is now Pionaarr Mods: plainer text, a look taken from the player, a record and a 3-band waveform running in the background.",
"New before/after shots: the same spot, stock against every mod including the ones in testing.",
"Builder: a Stock firmware switch that clears and locks the mods, and gives your picks back when switched off."
]
},
{
"version": "1.3.0",
"date": "2026-10-20",
+15 -11
View File
@@ -3,23 +3,23 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CDJ-900NXS Mods Builder</title>
<title>Pionaarr Mods Builder</title>
<meta name="description" content="Build your own modded C900NXS.UPD in the browser: upload the official 1.31 update, tick the mods, download the file. Nothing leaves your computer.">
<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=Inter:wght@400;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=1.3.0">
<script src="assets/versions.js?v=1.3.0"></script>
<script src="script.js?v=1.3.0"></script>
<script src="builder/data.js?v=1.3.0"></script>
<script src="builder/builder.js?v=1.3.0"></script>
<script src="builder/ui.js?v=1.3.0"></script>
<link href="https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&family=Chakra+Petch:wght@600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=1.4.0">
<script src="assets/versions.js?v=1.4.0"></script>
<script src="script.js?v=1.4.0"></script>
<script src="builder/data.js?v=1.4.0"></script>
<script src="builder/builder.js?v=1.4.0"></script>
<script src="builder/ui.js?v=1.4.0"></script>
</head>
<body>
<header class="top">
<div class="wrap">
<a class="brand" href="index.html"><span class="disc" aria-hidden="true"></span><span>CDJ-900NXS Mods</span></a>
<a class="brand" href="index.html"><span class="disc" aria-hidden="true"></span><span>Pionaarr Mods</span></a>
<nav class="tabs" aria-label="Sections">
<a href="index.html">Mods</a>
<a href="builder.html" aria-current="page">Builder</a>
@@ -33,7 +33,7 @@
<main class="wrap builder">
<div class="eyebrow">Builder</div>
<h1>Build your update</h1>
<p class="lead">Upload the official CDJ-900NXS <strong>1.31</strong> update, tick the mods and download your <code>C900NXS.UPD</code>. Everything runs in your browser: the file never leaves your computer.</p>
<p class="lead">Choose the official CDJ-900NXS <strong>1.31</strong> update, tick the mods, download your <code>C900NXS.UPD</code>. It all runs in your browser.</p>
<!-- 1 -->
<section class="step" id="step-file">
@@ -54,7 +54,11 @@
<div class="step-n">2</div>
<div class="step-body">
<h2>The mods</h2>
<p>Ticking a mod also ticks what it needs; unticking one unticks what needs it. Tick none for the stock firmware (relabelled so the player takes it).</p>
<p>Ticking a mod also ticks what it needs; unticking one unticks what needs it.</p>
<div class="picks-bar">
<button class="stock-btn" type="button" id="stock" aria-pressed="false"><span class="sw" aria-hidden="true"></span>Stock firmware</button>
<span class="hint">On: no mods, the official 1.31 relabelled so the player takes it — the way back.</span>
</div>
<div class="picks" id="picks"></div>
<p class="hint" id="pick-note"></p>
</div>
+17 -1
View File
@@ -24,6 +24,20 @@
];
var chosen = {};
MODS.forEach(function (m) { chosen[m.id] = m.state === 'stable'; });
var stock = false, before = null; // the stock switch, and the picks it put away
function setStock(on) {
stock = on;
if (on) {
before = {};
MODS.forEach(function (m) { before[m.id] = chosen[m.id]; chosen[m.id] = false; });
} else if (before) {
MODS.forEach(function (m) { chosen[m.id] = before[m.id]; });
}
byId('stock').setAttribute('aria-pressed', on ? 'true' : 'false');
byId('pick-note').textContent = on ? 'Stock: the update will have no mods.' : '';
render();
}
var file = null;
function byId(id) { return document.getElementById(id); }
@@ -56,7 +70,7 @@
box.innerHTML = '';
MODS.forEach(function (m) {
var row = document.createElement('label');
row.className = 'pick' + (chosen[m.id] ? ' on' : '');
row.className = 'pick' + (chosen[m.id] ? ' on' : '') + (stock ? ' off' : '');
row.innerHTML =
'<input type="checkbox"' + (chosen[m.id] ? ' checked' : '') + '>' +
'<span class="pick-main"><span class="pick-name">' + m.name + ' <code>' + m.id + '</code>' +
@@ -65,6 +79,7 @@
'<span class="pick-by">' + (m.by === 'ours' ? 'ours · made with Claude Code' : m.by === 'based' ? 'ours · based on arnoldasr, updated and published by us' : 'by arnoldasr') +
(m.needs ? ' · needs <code>' + m.needs + '</code>' : '') + '</span></span>' +
'<span class="state ' + m.state + '">' + m.state + '</span>';
row.querySelector('input').disabled = stock;
row.querySelector('input').addEventListener('change', function (e) { toggle(m.id, e.target.checked); });
box.appendChild(row);
});
@@ -130,5 +145,6 @@
byId('agree-risk').addEventListener('change', ready);
byId('agree-ai').addEventListener('change', ready);
byId('build').addEventListener('click', run);
byId('stock').addEventListener('click', function () { setStock(!stock); });
});
})();
+6 -6
View File
@@ -3,20 +3,20 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CDJ-900NXS Mods Docs</title>
<title>Pionaarr Mods Docs</title>
<meta name="description" content="How to build and install the CDJ-900NXS mods and how each one works, including the hot cue keys: play, gate, set and delete.">
<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=Inter:wght@400;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=1.3.0">
<script src="assets/versions.js?v=1.3.0"></script>
<script src="script.js?v=1.3.0"></script>
<link href="https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&family=Chakra+Petch:wght@600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=1.4.0">
<script src="assets/versions.js?v=1.4.0"></script>
<script src="script.js?v=1.4.0"></script>
</head>
<body>
<header class="top">
<div class="wrap">
<a class="brand" href="index.html"><span class="disc" aria-hidden="true"></span><span>CDJ-900NXS Mods</span></a>
<a class="brand" href="index.html"><span class="disc" aria-hidden="true"></span><span>Pionaarr Mods</span></a>
<nav class="tabs" aria-label="Sections">
<a href="index.html">Mods</a>
<a href="builder.html">Builder</a>
+33 -37
View File
@@ -3,20 +3,20 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CDJ-900NXS Mods</title>
<title>Pionaarr Mods</title>
<meta name="description" content="Firmware mods for the Pioneer CDJ-900NXS (firmware 1.31): RGB waveform, extended BPM, phase meter, cue colours, hot cues on the beat divide keys and more — compared with stock, built in your browser.">
<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=Inter:wght@400;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=1.3.0">
<script src="assets/versions.js?v=1.3.0"></script>
<script src="script.js?v=1.3.0"></script>
<link href="https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&family=Chakra+Petch:wght@600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=1.4.0">
<script src="assets/versions.js?v=1.4.0"></script>
<script src="script.js?v=1.4.0"></script>
</head>
<body>
<header class="top">
<div class="wrap">
<a class="brand" href="index.html"><span class="disc" aria-hidden="true"></span><span>CDJ-900NXS Mods</span></a>
<a class="brand" href="index.html"><span class="disc" aria-hidden="true"></span><span>Pionaarr Mods</span></a>
<nav class="tabs" aria-label="Sections">
<a href="index.html" aria-current="page">Mods</a>
<a href="builder.html">Builder</a>
@@ -30,39 +30,35 @@
<main>
<div class="hero">
<div class="wrap">
<div class="eyebrow">Pioneer CDJ-900NXS · firmware 1.31</div>
<h1>New tricks for an <span class="grad">old favourite</span>.</h1>
<p class="lead">
A set of firmware mods that bring features of newer players to the CDJ-900NXS:
an RGB waveform, a BPM to two decimals, a phase meter, rekordbox cue colours,
beat jump and <strong>hot cues you can play from the deck itself</strong>. Build your own update file
right here in the browser — the boot loader stays untouched.
</p>
<span class="lcd"><i></i>CDJ-900NXS · firmware 1.31</span>
<h1>Pionaarr <span class="r">Mods</span></h1>
<p class="lead">Firmware mods for the Pioneer CDJ-900NXS. We took the player's firmware apart in an emulator and added what newer Pioneer players have: hot cues on the deck, beat jump, colour and 3-band waveforms, keys in Camelot colours and more.</p>
<p class="lead">Pick the mods you want in the builder. It patches the official 1.31 update in your browser and gives you a normal <code>C900NXS.UPD</code> to install like any update. The boot loader is never touched, so you can always go back to stock.</p>
<div class="cta">
<a class="btn primary" href="builder.html">Build your update</a>
<a class="btn" href="#mods">See the mods</a>
<a class="btn" href="docs.html#hotcuekeys">Hot cue guide</a>
<a class="btn primary" href="builder.html">Open the builder</a>
<a class="btn" href="#mods">The mods</a>
<a class="btn" href="docs.html">Docs</a>
</div>
<div class="compare" style="--pos:50%">
<img src="assets/stock-full.png" alt="Stock CDJ-900NXS screen: blue waveform, red memory cues, no hot cues">
<img class="after" src="assets/mods-full.png" alt="Modded screen: RGB waveform, coloured cues, hot cues A to F, HOT KEYS box">
<img src="assets/stock-full.png" alt="Stock CDJ-900NXS screen: blue waveform, red memory cues">
<img class="after" src="assets/mods-full.png" alt="Modded screen: 3-band waveform, coloured cues and hot cues, HOT KEYS, key in colour, BPM to two decimals">
<span class="tag l">Stock 1.31</span>
<span class="tag r">Modded</span>
<span class="tag r">Pionaarr Mods</span>
<span class="handle" aria-hidden="true"></span>
<input type="range" min="0" max="100" value="50" aria-label="Drag to compare stock and modded screen">
</div>
<div class="caption">Same track, same position (2:13 into “Webra – Jumbled”). Drag to compare. Screens taken in the CDJ-900NXS emulator the mods were developed in.</div>
<div class="caption">Same track, same spot, in the emulator. Right: every mod, including the ones still in testing (the 3-band waveform is one of them).</div>
</div>
</div>
<section>
<div class="wrap">
<div class="strip">
<div class="tile"><div class="n">7</div><div class="t">mods, pick the ones you want</div></div>
<div class="tile"><div class="n">A–C</div><div class="t">hot cues on the BEAT DIVIDE keys, loaded in the background</div></div>
<div class="tile"><div class="n">0 bytes</div><div class="t">changed in the boot loader and recovery</div></div>
<div class="tile"><div class="n">1 file</div><div class="t">a normal <code>C900NXS.UPD</code>, built in your browser</div></div>
<div class="tile"><div class="n">9</div><div class="t">mods in the builder, plus more in testing</div></div>
<div class="tile"><div class="n">A–C</div><div class="t">hot cues on the BEAT DIVIDE keys</div></div>
<div class="tile"><div class="n">0 bytes</div><div class="t">changed in the boot loader</div></div>
<div class="tile"><div class="n">1 file</div><div class="t">built in your browser, nothing uploaded</div></div>
</div>
</div>
</section>
@@ -70,7 +66,7 @@
<section id="mods">
<div class="wrap">
<h2 class="section-title">The mods</h2>
<p class="section-sub">Each one compared with stock. Two are by <a href="https://github.com/arnoldasr/cdj900nxs">arnoldasr</a>, one is based on his work and now ours, the rest are ours. Choose any combination in the <a href="builder.html">builder</a>.</p>
<p class="section-sub">Each one next to stock. Two are by <a href="https://github.com/arnoldasr/cdj900nxs">arnoldasr</a>, one is based on his work, the rest are ours.</p>
<div class="legend">
<span class="state stable">stable</span>
<span class="state experimental">experimental</span>
@@ -89,7 +85,7 @@
</div>
<div class="meta"><span class="player">ran on a player</span><span class="state stable">stable</span></div>
</div>
<p>The big waveform in RGB colours, as on newer Pioneer players, instead of plain blue — and the whole-track overview at the bottom as a warm red-to-white gradient, so drops and breaks stand out. It started as arnoldasr's <code>rgb_waveform</code>; we took it over and keep it up to date (1.0.1: rekordbox's colours now also on tracks whose colour data is a little shorter than the waveform — before, those fell back to plain gradient colours).</p>
<p>The big waveform in rekordbox's RGB colours, the overview as a red-to-white gradient. Based on arnoldasr's <code>rgb_waveform</code>, now maintained by us (1.0.1 fixes tracks that showed only gradient colours).</p>
<div class="vs">
<figure><img src="assets/detail-stock.png" alt="Stock blue detail waveform"><figcaption>Stock</figcaption></figure>
<figure class="mod"><img src="assets/detail-mods.png" alt="RGB detail waveform"><figcaption>RGB</figcaption></figure>
@@ -114,7 +110,7 @@
</div>
<div class="meta"><span class="player">ran on a player</span><span class="state stable">stable</span></div>
</div>
<p>The BPM to two decimals: the tenths move into a small digit above, the hundredths take the place below — handy for matching tempos by ear and by eye.</p>
<p>The BPM to two decimals: tenths small above, hundredths below.</p>
<div class="vs">
<figure><img src="assets/bpm-stock.png" alt="Stock BPM 145.0"><figcaption>Stock</figcaption></figure>
<figure class="mod"><img src="assets/bpm-mods.png" alt="BPM with two decimals"><figcaption>Two decimals</figcaption></figure>
@@ -135,7 +131,7 @@
</div>
<div class="meta"><span class="player">ran on a player</span><span class="state stable">stable</span></div>
</div>
<p>A two-line phase meter instead of the four boxes: orange for the master, blue for this deck, each with a marker that sweeps along the bar. Switched in UTILITY, where the rarely used SLIP FLASHING item becomes PHASE METER.</p>
<p>Two lines instead of four boxes: orange for the master, blue for this deck, a marker sweeping along the bar. UTILITY › PHASE METER switches it.</p>
<div class="vs">
<figure><img src="assets/phase-stock.png" alt="Stock phase meter: four boxes"><figcaption>Stock</figcaption></figure>
<figure class="mod"><img src="assets/phase-mods.png" alt="Two-line phase meter"><figcaption>Two lines</figcaption></figure>
@@ -159,7 +155,7 @@
</div>
<div class="meta"><span class="player">ran on a player</span><span class="state stable">stable</span></div>
</div>
<p>Memory cues take the colour you gave them in rekordbox. The hot cues stored on the USB (A–H) appear as lettered boxes in their rekordbox colour on the overview and the big waveform — the stock 900NXS ignores them completely.</p>
<p>Memory cues in their rekordbox colours, and the hot cues on the stick (A–H) as lettered boxes on both waveforms.</p>
<div class="vs">
<figure><img src="assets/detail-stock.png" alt="Stock: red memory triangles only"><figcaption>Stock</figcaption></figure>
<figure class="mod"><img src="assets/hotcue-detail.png" alt="Hot cue A box on the detail waveform"><figcaption>Hot cue A on the waveform</figcaption></figure>
@@ -181,7 +177,7 @@
</div>
<div class="meta"><span class="player">ran on a player</span><span class="state experimental">experimental</span></div>
</div>
<p>Play your rekordbox hot cues on the deck. A short press of QUANTIZE switches the hot key mode; the top three BEAT DIVIDE keys become hot cues A–C. After a track loads they are buffered in the background, so each starts at once — also from a pause, in CDJ and VINYL mode. Hold for a gate, set new ones while paused, delete them with DELETE; changes are saved on the USB with colours (A green, B orange, C blue).</p>
<p>QUANTIZE pressed short switches the hot key mode: the top three BEAT DIVIDE keys play hot cues A–C, instantly. Hold for a gate, hold on an empty key to set one, DELETE + key to delete. Saved on the stick in colour.</p>
<div class="vs">
<figure><img src="assets/display-stock.png" alt="Stock: QUANTIZE label only"><figcaption>Stock</figcaption></figure>
<figure class="mod"><img src="assets/display-mods.png" alt="HOT KEYS box beside QUANTIZE"><figcaption>HOT KEYS mode on</figcaption></figure>
@@ -206,7 +202,7 @@
</div>
<div class="meta"><span class="player">ran on a player</span><span class="state experimental">experimental</span></div>
</div>
<p>TRACK SEARCH ◀◀ / ▶▶ jumps 1, 2, 4 or 8 beats along the rekordbox beat grid instead of changing the track — at once, without a gap and in phase with the other deck, as on the newest players. Chosen in UTILITY › BEAT JUMP (default 4 beats, kept when the player is off). Works playing, paused and at the cue. The jump is made in the audio the player has already decoded; a press it cannot jump yet (clicked very fast with 8 beats) is simply ignored instead of running the player dry.</p>
<p>TRACK SEARCH ◀◀ ▶▶ jumps 1, 2, 4 or 8 beats on the beat grid, gapless and in phase. Set in UTILITY › BEAT JUMP.</p>
<ul class="diff">
<li><span class="k">Stock</span><span>TRACK SEARCH goes to the previous / next track.</span></li>
<li><span class="k new">Modded</span><span>Whole bars (4, 8) or beats (1, 2) forward and back, the phase meter stays where it was.</span></li>
@@ -224,7 +220,7 @@
</div>
<div class="meta"><span class="player">ran on a player</span><span class="state stable">stable</span></div>
</div>
<p>When the screen saver dims the display, the deck comes alive: the BEAT DIVIDE keys fade in a wave, SYNC and MASTER pulse against each other, the transport keys light one after another and the jog's three rings run inner, middle, outer and back — a loop of about four and a half seconds. Any key ends it. UTILITY › LED ANIMATION, on from the first start.</p>
<p>While the screen saver dims the display, the button LEDs and the jog rings play a loop. Any key stops it.</p>
<ul class="diff">
<li><span class="k">Stock</span><span>The screen saver only dims the display.</span></li>
<li><span class="k new">Modded</span><span>Button LEDs and jog rings animate while it is dimmed.</span></li>
@@ -242,7 +238,7 @@
</div>
<div class="meta"><span class="state experimental">experimental</span></div>
</div>
<p>Every track's key in one notation, whatever rekordbox exported: Camelot (8A), Open Key (1m) or classic (Am), chosen in UTILITY › KEY NOTATION. The player converts on its own with a table of all spellings, sharps and flats included — on the main screen, in INFO and in BROWSE › KEY. The key shows in the colour of its place on the Camelot wheel.</p>
<p>Every key in one notation, whatever rekordbox exported: Camelot, Open Key or classic, in its Camelot wheel colour. Also in INFO and BROWSE › KEY.</p>
<div class="vs stack">
<figure><img src="assets/key-stock.png" alt="Key as exported: 4A"><figcaption>As exported (Camelot): 4A</figcaption></figure>
<figure class="mod"><img src="assets/key-mods.png" alt="Key in Open Key: 9m"><figcaption>KEY NOTATION › OPEN KEY: 9m</figcaption></figure>
@@ -266,7 +262,7 @@
</div>
<div class="meta"><span class="player">ran on a player</span><span class="state stable">stable</span></div>
</div>
<p>The boot screen tells you what you are running: <em>Pionaarr Mods 1.0</em> instead of <em>Ver 1.31</em>. Leave it out to keep the stock text.</p>
<p>The boot screen says <em>Pionaarr Mods 1.0</em> instead of <em>Ver 1.31</em>.</p>
<div class="vs">
<figure><img src="assets/boot-stock.png" alt="Stock boot screen: Ver 1.31"><figcaption>Stock</figcaption></figure>
<figure class="mod"><img src="assets/boot-mods.png" alt="Boot screen: Pionaarr Mods 1.0"><figcaption>Pionaarr Mods 1.0</figcaption></figure>
@@ -274,14 +270,14 @@
</article>
</div>
<p class="caption">More in the works — beat jump, coloured Camelot keys, stems and a 3-band waveform: see <a href="upcoming.html">Upcoming</a>.</p>
<p class="caption">In testing: quick search, USB auto load, a reversed phase meter and the 3-band waveform — see <a href="upcoming.html">Upcoming</a>.</p>
</div>
</section>
<section id="build">
<div class="wrap">
<h2 class="section-title">Build it in your browser</h2>
<p class="section-sub">The <a href="builder.html">builder</a> takes the official 1.31 update, adds the mods you tick and gives you a new <code>C900NXS.UPD</code> — all on your computer, nothing is uploaded. Only the main application changes; the boot loader, the recovery loader and the other sections are copied byte for byte and checked again before you can download the file.</p>
<p class="section-sub">The <a href="builder.html">builder</a> takes the official 1.31 update and adds the mods you tick, on your computer. Only the main application changes; the boot loader and recovery are copied byte for byte and checked.</p>
<div class="cta"><a class="btn primary" href="builder.html">Open the builder</a></div>
<p class="section-sub" style="margin-top:28px">Prefer the command line? The Python patcher builds the same file, byte for byte:</p>
<div class="term"><span class="c"># pick the mods in a list</span>
+73
View File
@@ -85,3 +85,76 @@
}
});
})();
// Background: a record turning at the top right, a 3-band waveform (lows blue,
// mids amber, highs white) running along the bottom at 128 BPM; --beat pulses
// with it. Quiet when the system asks for reduced motion or the tab is hidden.
(function () {
var BPM = 128, COL = 4, still = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
function rnd(k) { var x = Math.sin(k * 127.1 + 311.7) * 43758.5453; return x - Math.floor(x); }
// one column of the waveform: k counts columns, 16 to a beat
function band(k) {
var beat = k % 16, bar = Math.floor(k / 64) % 16, drop = bar % 8 >= 2 && bar % 8 < 7;
var kick = drop ? Math.exp(-beat / 3.2) : 0;
var low = 0.12 + 0.8 * kick + 0.08 * rnd(k);
var mid = (drop ? 0.32 : 0.45) + 0.25 * rnd(k + 9) * (0.6 + 0.4 * Math.sin(k / 40));
var high = (beat >= 7 && beat <= 9 ? 0.45 : 0.08) * (0.5 + rnd(k + 3));
return [low, mid, high];
}
document.addEventListener('DOMContentLoaded', function () {
var c = document.createElement('canvas'), x = c.getContext('2d');
if (!x) return;
c.className = 'bg-fx'; c.setAttribute('aria-hidden', 'true');
document.body.insertBefore(c, document.body.firstChild);
var w, h, dpr, last = 0, t0 = performance.now();
function size() {
dpr = Math.min(window.devicePixelRatio || 1, 1.5);
w = innerWidth; h = innerHeight;
c.width = Math.round(w * dpr); c.height = Math.round(h * dpr);
}
size(); addEventListener('resize', size);
function draw(now) {
var light = document.documentElement.getAttribute('data-theme') === 'light', a = light ? 0.5 : 1;
var t = (now - t0) / 1000, beats = t * BPM / 60;
x.setTransform(dpr, 0, 0, dpr, 0, 0);
x.clearRect(0, 0, w, h);
// the record
var r = Math.max(w, h) * 0.32, cx = w - r * 0.35, cy = r * 0.25, turn = still ? 0.6 : t * Math.PI * 2 * 0.09;
x.save(); x.translate(cx, cy);
for (var g = 0.36; g < 1; g += 0.022) {
x.beginPath(); x.arc(0, 0, r * g, 0, Math.PI * 2);
x.strokeStyle = light ? 'rgba(20,24,33,' + 0.05 * a + ')' : 'rgba(255,255,255,' + 0.035 + ')';
x.lineWidth = 1; x.stroke();
}
x.rotate(turn);
var sheen = x.createLinearGradient(-r, 0, r, 0);
sheen.addColorStop(0.42, 'rgba(255,255,255,0)');
sheen.addColorStop(0.5, light ? 'rgba(20,24,33,' + 0.06 * a + ')' : 'rgba(255,255,255,0.06)');
sheen.addColorStop(0.58, 'rgba(255,255,255,0)');
x.beginPath(); x.arc(0, 0, r, 0, Math.PI * 2); x.arc(0, 0, r * 0.33, 0, Math.PI * 2, true);
x.fillStyle = sheen; x.fill();
x.beginPath(); x.arc(0, 0, r * 0.3, 0, Math.PI * 2);
x.fillStyle = 'rgba(255,90,31,' + 0.10 * a + ')'; x.fill();
x.beginPath(); x.arc(r * 0.18, 0, r * 0.012, 0, Math.PI * 2);
x.fillStyle = 'rgba(255,255,255,' + 0.25 * a + ')'; x.fill();
x.restore();
// the waveform, 16 columns to a beat, scrolling left
var y0 = h - Math.min(110, h * 0.13), amp = Math.min(70, h * 0.085);
var shift = still ? 0 : beats * 16 * COL, first = Math.floor(shift / COL), off = shift - first * COL;
for (var i = 0; i * COL - off < w + COL; i++) {
var b = band(first + i), px = i * COL - off;
var hl = b[0] * amp, hm = b[1] * amp * 0.85, hh = b[2] * amp;
x.fillStyle = 'rgba(40,110,255,' + 0.22 * a + ')'; x.fillRect(px, y0 - hl, COL - 1, hl * 2);
x.fillStyle = 'rgba(255,150,30,' + 0.22 * a + ')'; x.fillRect(px, y0 - hm, COL - 1, hm * 2);
x.fillStyle = 'rgba(255,255,255,' + 0.20 * a + ')'; x.fillRect(px, y0 - hh, COL - 1, hh * 2);
}
document.documentElement.style.setProperty('--beat', still ? 0 : Math.pow(1 - (beats % 1), 3).toFixed(3));
}
function loop(now) {
if (!document.hidden && now - last > 33) { last = now; draw(now); }
if (!still) requestAnimationFrame(loop);
}
if (still) draw(performance.now()); else requestAnimationFrame(loop);
addEventListener('resize', function () { if (still) draw(performance.now()); });
});
})();
+83
View File
@@ -412,3 +412,86 @@ button.btn { font: inherit; font-weight: 700; cursor: pointer; }
.ver-btn { padding: 6px 7px; font-size: .68rem; }
.tabs a { padding: 6px 10px; }
}
/* ---- Pionaarr look: CDJ hardware, LCD readouts, a turning record ---- */
:root {
--display: "Chakra Petch", "Barlow", system-ui, sans-serif;
--sans: "Barlow", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--lcd: #ff7a2a;
--lcd-green: #8dffb4;
--beat: 0;
}
:root[data-theme="light"] { --lcd: #d9520c; --lcd-green: #0a8f45; }
html { background: var(--bg); }
body { background: transparent; font-size: 17px; }
.bg-fx { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; }
h1, h2, h3, .brand, .tile .n { font-family: var(--display); }
.hero::before { display: none; }
.brand .disc { animation: spin 1.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.brand .disc { background: repeating-radial-gradient(circle, #1a1d26 0 2px, #2a2f3c 2px 3px); box-shadow: inset 0 0 0 9px #11141b, 0 0 0 2px var(--accent); position: relative; }
.brand .disc::after { content: ""; position: absolute; inset: 11px; border-radius: 50%; background: var(--accent); }
.tabs { background: none; border: 0; gap: 2px; padding: 0; }
.tabs a {
font: 600 .8rem/1 var(--mono); letter-spacing: .14em; text-transform: uppercase;
border-radius: 0; padding: 12px 12px 10px; color: var(--faint);
}
.tabs a:hover { color: var(--text); }
.tabs a[aria-current="page"] {
background: none; color: var(--lcd-green); box-shadow: inset 0 -2px 0 var(--lcd-green);
text-shadow: 0 0 10px color-mix(in srgb, var(--lcd-green) 65%, transparent);
}
.lcd {
display: inline-flex; align-items: center; gap: 12px; font: 700 .82rem/1 var(--mono);
color: var(--lcd); letter-spacing: .16em; text-transform: uppercase; padding: 9px 12px;
border: 1px solid color-mix(in srgb, var(--lcd) 40%, transparent); border-radius: 6px;
background: color-mix(in srgb, #000 55%, transparent);
text-shadow: 0 0 calc(6px + 6px * var(--beat)) color-mix(in srgb, var(--lcd) 70%, transparent);
}
:root[data-theme="light"] .lcd { background: #fff8f2; }
.lcd i { width: 7px; height: 7px; border-radius: 50%; background: var(--lcd); opacity: calc(.35 + .65 * var(--beat)); }
.hero { padding: 64px 0 36px; }
.hero h1 {
font-size: clamp(2.8rem, 8vw, 5.6rem); line-height: .95; letter-spacing: .01em;
text-transform: uppercase; margin: 22px 0 18px; font-weight: 700;
}
.hero h1 .r { color: var(--accent); }
.hero p.lead { font-size: 1.18rem; color: var(--muted); max-width: 680px; margin: 0 0 12px; }
.hero p.lead + p.lead { margin-bottom: 26px; }
.btn { border-radius: 8px; font-family: var(--display); letter-spacing: .03em; text-transform: uppercase; font-size: .92rem; }
.card { border-radius: 10px; background: color-mix(in srgb, var(--card) 90%, transparent); backdrop-filter: blur(6px); }
.card h3 { font-weight: 700; letter-spacing: .01em; }
.tile { background: color-mix(in srgb, var(--card) 85%, transparent); border-radius: 10px; }
.tile .n { color: var(--lcd); font-weight: 700; }
h2.section-title { text-transform: uppercase; letter-spacing: .02em; }
/* builder: the stock switch */
.picks-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 4px 0 14px; }
.stock-btn {
display: inline-flex; align-items: center; gap: 10px; font: 700 .78rem/1 var(--mono); letter-spacing: .12em;
text-transform: uppercase; padding: 10px 14px; border-radius: 8px; border: 1px solid var(--line);
background: var(--card); color: var(--muted); cursor: pointer;
}
.stock-btn:hover { color: var(--text); }
.stock-btn .sw { width: 30px; height: 16px; border-radius: 999px; background: var(--line); position: relative; transition: background .2s; flex: none; }
.stock-btn .sw::after {
content: ""; position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%;
background: #fff; transition: transform .2s;
}
.stock-btn[aria-pressed="true"] { color: var(--text); border-color: var(--accent-2); }
.stock-btn[aria-pressed="true"] .sw { background: var(--accent-2); }
.stock-btn[aria-pressed="true"] .sw::after { transform: translateX(14px); }
.pick.off { opacity: .38; filter: grayscale(1); cursor: not-allowed; }
.pick.off input { cursor: not-allowed; }
@media (max-width: 560px) {
.tabs a { padding: 10px 7px 8px; letter-spacing: .06em; font-size: .72rem; }
}
@media (prefers-reduced-motion: reduce) {
.brand .disc { animation: none; }
}
+9
View File
@@ -1,6 +1,15 @@
{
"_about": "The site's changelog, newest first. Versions follow Semantic Versioning (MAJOR.MINOR.PATCH), dates ISO 8601 (DIN EN 28601). make_builder_data.py adds every mod's current version from its module and writes assets/versions.js.",
"site": [
{
"version": "1.4.0",
"date": "2026-10-20",
"changes": [
"The site is now Pionaarr Mods: plainer text, a look taken from the player, a record and a 3-band waveform running in the background.",
"New before/after shots: the same spot, stock against every mod including the ones in testing.",
"Builder: a Stock firmware switch that clears and locks the mods, and gives your picks back when switched off."
]
},
{
"version": "1.3.0",
"date": "2026-10-20",
+6 -6
View File
@@ -3,20 +3,20 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CDJ-900NXS Mods Upcoming</title>
<title>Pionaarr Mods Upcoming</title>
<meta name="description" content="What comes next for the CDJ-900NXS mods: quick search, USB auto load, a reversed phase meter, stems and a 3-band waveform.">
<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=Inter:wght@400;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=1.3.0">
<script src="assets/versions.js?v=1.3.0"></script>
<script src="script.js?v=1.3.0"></script>
<link href="https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&family=Chakra+Petch:wght@600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=1.4.0">
<script src="assets/versions.js?v=1.4.0"></script>
<script src="script.js?v=1.4.0"></script>
</head>
<body>
<header class="top">
<div class="wrap">
<a class="brand" href="index.html"><span class="disc" aria-hidden="true"></span><span>CDJ-900NXS Mods</span></a>
<a class="brand" href="index.html"><span class="disc" aria-hidden="true"></span><span>Pionaarr Mods</span></a>
<nav class="tabs" aria-label="Sections">
<a href="index.html">Mods</a>
<a href="builder.html">Builder</a>