13 CSS Neumorphism & Soft UI Designs
Audio Control Suite
A warm-parchment audio mixing console. Interactive range-input scrubber overlaid on a 49-bar waveform that lights up burnt-gold as you drag through the track. The center volume knob is mouse-draggable with SVG tick marks rendered around its perimeter. Five vertical EQ faders are individually draggable with live dB labels (32Hz, 250Hz, 1kHz, 4kHz, 16kHz). Six rotary mini knobs along the bottom row control Bass, Treble, Reverb, Delay, Width, Drive. Playfair Display + Space Mono. Best for music production tools, DAW dashboards, audio plugins.
The code
<section class="nm-aud" aria-label="Audio control suite">
<div class="card">
<div class="track-header">
<div>
<div class="track-title">Ember & Ash</div>
<div class="track-sub">SOLARIS & THE ECHO · LIMINAL HOURS</div>
</div>
<div class="track-bpm">BPM <span>124</span></div>
</div>
<div class="waveform-row" data-nm-aud-wave aria-hidden="true"></div>
<div class="scrubber-wrap">
<div class="scrubber-label-row">
<span data-nm-aud-time-now>1:42</span>
<span>3:47</span>
</div>
<div class="scrubber-track">
<div class="scrubber-glow" data-nm-aud-glow></div>
<div class="scrubber-fill" data-nm-aud-fill></div>
<div class="scrubber-thumb" data-nm-aud-thumb></div>
<input type="range" class="scrubber" data-nm-aud-scrub min="0" max="100" value="38" aria-label="Track progress" />
</div>
</div>
<div class="main-controls">
<div class="play-controls">
<button type="button" class="ctrl ctrl-sm" aria-label="Shuffle">⇄</button>
<button type="button" class="ctrl ctrl-md" aria-label="Previous">⏮</button>
<button type="button" class="ctrl ctrl-play" data-nm-aud-play aria-label="Play / Pause">▶</button>
<button type="button" class="ctrl ctrl-md" aria-label="Next">⏭</button>
<button type="button" class="ctrl ctrl-sm" aria-label="Loop">↻</button>
</div>
<div class="vol-knob-wrap">
<div class="vol-knob" data-nm-aud-volknob>
<svg class="vol-tick-svg" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" data-nm-aud-volticks aria-hidden="true"></svg>
<div class="vol-knob-inner"></div>
<div class="vol-knob-dot" data-nm-aud-voldot></div>
</div>
<div class="mini-lbl">Volume</div>
<div class="vol-val" data-nm-aud-volval>72</div>
</div>
<div class="eq-wrap" data-nm-aud-eq></div>
</div>
<div class="knobs-row" data-nm-aud-knobs></div>
</div>
</section> <section class="nm-aud" aria-label="Audio control suite">
<div class="card">
<div class="track-header">
<div>
<div class="track-title">Ember & Ash</div>
<div class="track-sub">SOLARIS & THE ECHO · LIMINAL HOURS</div>
</div>
<div class="track-bpm">BPM <span>124</span></div>
</div>
<div class="waveform-row" data-nm-aud-wave aria-hidden="true"></div>
<div class="scrubber-wrap">
<div class="scrubber-label-row">
<span data-nm-aud-time-now>1:42</span>
<span>3:47</span>
</div>
<div class="scrubber-track">
<div class="scrubber-glow" data-nm-aud-glow></div>
<div class="scrubber-fill" data-nm-aud-fill></div>
<div class="scrubber-thumb" data-nm-aud-thumb></div>
<input type="range" class="scrubber" data-nm-aud-scrub min="0" max="100" value="38" aria-label="Track progress" />
</div>
</div>
<div class="main-controls">
<div class="play-controls">
<button type="button" class="ctrl ctrl-sm" aria-label="Shuffle">⇄</button>
<button type="button" class="ctrl ctrl-md" aria-label="Previous">⏮</button>
<button type="button" class="ctrl ctrl-play" data-nm-aud-play aria-label="Play / Pause">▶</button>
<button type="button" class="ctrl ctrl-md" aria-label="Next">⏭</button>
<button type="button" class="ctrl ctrl-sm" aria-label="Loop">↻</button>
</div>
<div class="vol-knob-wrap">
<div class="vol-knob" data-nm-aud-volknob>
<svg class="vol-tick-svg" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" data-nm-aud-volticks aria-hidden="true"></svg>
<div class="vol-knob-inner"></div>
<div class="vol-knob-dot" data-nm-aud-voldot></div>
</div>
<div class="mini-lbl">Volume</div>
<div class="vol-val" data-nm-aud-volval>72</div>
</div>
<div class="eq-wrap" data-nm-aud-eq></div>
</div>
<div class="knobs-row" data-nm-aud-knobs></div>
</div>
</section>/* ─── 05 Audio Control Suite — warm parchment audio console ───────── */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,500;0,700;1,400&family=Space+Mono:wght@400;700&display=swap');
.nm-aud {
--nm-aud-bg: #f0e6d3;
--nm-aud-sd: #d0c0a0;
--nm-aud-sl: #fffaf0;
--nm-aud-ib: #e8ddc8;
--nm-aud-acc: #c8783a;
--nm-aud-ac2: #e0a858;
--nm-aud-ac3: #8a4a20;
--nm-aud-txt: #7a6a58;
--nm-aud-txt2:#2a1e10;
position: relative;
width: 100%;
min-height: 760px;
background: var(--nm-aud-bg);
font-family: 'Space Mono', ui-monospace, monospace;
color: var(--nm-aud-txt);
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
overflow: hidden;
box-sizing: border-box;
}
.nm-aud *,
.nm-aud *::before,
.nm-aud *::after { box-sizing: border-box; }
.nm-aud .card {
position: relative;
width: 100%;
max-width: 860px;
background: var(--nm-aud-bg);
border-radius: 44px;
padding: 44px 40px 38px;
box-shadow: 24px 24px 56px var(--nm-aud-sd), -24px -24px 56px var(--nm-aud-sl);
display: flex;
flex-direction: column;
gap: 30px;
}
/* Track header */
.nm-aud .track-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.nm-aud .track-title {
font-family: 'Playfair Display', Georgia, serif;
font-size: 26px;
font-weight: 700;
color: var(--nm-aud-txt2);
letter-spacing: -0.5px;
}
.nm-aud .track-sub {
font-size: 11px;
color: var(--nm-aud-txt);
margin-top: 4px;
letter-spacing: 0.5px;
}
.nm-aud .track-bpm {
background: var(--nm-aud-bg);
border-radius: 12px;
padding: 10px 18px;
box-shadow: inset 5px 5px 10px var(--nm-aud-sd), inset -5px -5px 10px var(--nm-aud-sl);
font-size: 11px;
color: var(--nm-aud-txt);
}
.nm-aud .track-bpm span {
color: var(--nm-aud-acc);
font-weight: 700;
font-size: 15px;
}
/* Waveform */
.nm-aud .waveform-row {
display: flex;
align-items: center;
gap: 3px;
height: 36px;
margin-bottom: 6px;
}
.nm-aud .wf-bar {
flex: 1;
border-radius: 2px;
background: linear-gradient(to top, var(--nm-aud-sd), var(--nm-aud-ib));
opacity: 0.45;
min-height: 3px;
}
.nm-aud .wf-bar.played {
background: linear-gradient(to top, var(--nm-aud-acc), var(--nm-aud-ac2));
opacity: 0.7;
}
/* Scrubber */
.nm-aud .scrubber-wrap { position: relative; }
.nm-aud .scrubber-label-row {
display: flex;
justify-content: space-between;
font-size: 10px;
color: var(--nm-aud-txt);
margin-bottom: 10px;
letter-spacing: 0.5px;
}
.nm-aud .scrubber-track {
width: 100%;
height: 10px;
border-radius: 5px;
background: var(--nm-aud-ib);
box-shadow: inset 5px 5px 10px var(--nm-aud-sd), inset -5px -5px 10px var(--nm-aud-sl);
position: relative;
cursor: pointer;
}
.nm-aud .scrubber-fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 38%;
border-radius: 5px;
background: linear-gradient(90deg, var(--nm-aud-ac3), var(--nm-aud-acc), var(--nm-aud-ac2));
pointer-events: none;
}
.nm-aud .scrubber-glow {
position: absolute;
top: -4px;
left: 0;
height: 18px;
width: 38%;
border-radius: 9px;
background: linear-gradient(90deg, transparent, rgba(200, 120, 58, 0.3));
filter: blur(6px);
pointer-events: none;
}
.nm-aud .scrubber {
position: absolute;
top: -4px;
left: 0;
width: 100%;
height: 18px;
opacity: 0;
cursor: pointer;
margin: 0;
z-index: 10;
}
.nm-aud .scrubber-thumb {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 26px;
height: 26px;
border-radius: 50%;
background: var(--nm-aud-bg);
box-shadow: 5px 5px 12px var(--nm-aud-sd), -5px -5px 12px var(--nm-aud-sl);
left: 38%;
pointer-events: none;
transition: box-shadow 0.15s;
}
.nm-aud .scrubber-thumb::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
border-radius: 50%;
background: linear-gradient(145deg, var(--nm-aud-acc), var(--nm-aud-ac2));
box-shadow: 0 0 6px rgba(200, 120, 58, 0.5);
}
/* Main controls grid */
.nm-aud .main-controls {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 28px;
align-items: center;
}
/* Play controls */
.nm-aud .play-controls {
display: flex;
gap: 12px;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
}
.nm-aud .ctrl {
background: var(--nm-aud-bg);
border: none;
cursor: pointer;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: 7px 7px 15px var(--nm-aud-sd), -7px -7px 15px var(--nm-aud-sl);
transition: all 0.15s ease;
color: var(--nm-aud-txt2);
font-size: 16px;
}
.nm-aud .ctrl:active {
box-shadow: inset 4px 4px 10px var(--nm-aud-sd), inset -4px -4px 10px var(--nm-aud-sl);
}
.nm-aud .ctrl:hover { transform: scale(1.05); }
.nm-aud .ctrl-sm { width: 42px; height: 42px; }
.nm-aud .ctrl-md { width: 52px; height: 52px; font-size: 18px; }
.nm-aud .ctrl-play {
width: 66px;
height: 66px;
font-size: 22px;
background: linear-gradient(145deg, var(--nm-aud-acc), var(--nm-aud-ac3));
color: #fffaf0;
box-shadow: 10px 10px 22px rgba(200, 120, 58, 0.4), -6px -6px 16px var(--nm-aud-sl);
}
.nm-aud .ctrl-play:hover { transform: scale(1.05); }
.nm-aud .ctrl-play:active {
box-shadow: inset 6px 6px 14px rgba(0, 0, 0, 0.2);
transform: scale(0.97);
}
/* Volume knob */
.nm-aud .vol-knob-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.nm-aud .vol-knob {
width: 110px;
height: 110px;
border-radius: 50%;
background: var(--nm-aud-bg);
box-shadow: 14px 14px 32px var(--nm-aud-sd), -14px -14px 32px var(--nm-aud-sl);
position: relative;
cursor: grab;
user-select: none;
}
.nm-aud .vol-knob:active { cursor: grabbing; }
.nm-aud .vol-knob-inner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 70px;
height: 70px;
border-radius: 50%;
background: var(--nm-aud-ib);
box-shadow: inset 6px 6px 14px var(--nm-aud-sd), inset -6px -6px 14px var(--nm-aud-sl);
}
.nm-aud .vol-knob-dot {
position: absolute;
top: 12px;
left: 50%;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--nm-aud-acc);
transform: translateX(-50%);
transform-origin: 3px 43px;
box-shadow: 0 0 8px var(--nm-aud-acc);
transition: transform 0.05s;
}
.nm-aud .vol-tick-svg { position: absolute; inset: 0; }
.nm-aud .mini-lbl {
font-size: 9px;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--nm-aud-txt);
}
.nm-aud .vol-val {
font-size: 20px;
font-weight: 700;
color: var(--nm-aud-txt2);
margin-top: -6px;
font-family: 'Space Mono', monospace;
}
/* EQ faders */
.nm-aud .eq-wrap {
display: flex;
gap: 14px;
align-items: flex-end;
justify-content: flex-end;
}
.nm-aud .fader-group {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.nm-aud .fader {
position: relative;
width: 28px;
height: 110px;
}
.nm-aud .fader-track {
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 8px;
height: 100%;
border-radius: 4px;
background: var(--nm-aud-ib);
box-shadow: inset 3px 3px 7px var(--nm-aud-sd), inset -3px -3px 7px var(--nm-aud-sl);
}
.nm-aud .fader-fill {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 8px;
bottom: 0;
border-radius: 4px;
background: linear-gradient(to top, var(--nm-aud-ac3), var(--nm-aud-acc), var(--nm-aud-ac2));
}
.nm-aud .fader-thumb {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 28px;
height: 20px;
border-radius: 6px;
background: var(--nm-aud-bg);
box-shadow: 4px 4px 8px var(--nm-aud-sd), -4px -4px 8px var(--nm-aud-sl);
cursor: ns-resize;
display: flex;
align-items: center;
justify-content: center;
}
.nm-aud .fader-thumb::before {
content: '';
display: block;
width: 14px;
height: 2px;
border-radius: 1px;
background: var(--nm-aud-sd);
box-shadow: 0 -4px 0 var(--nm-aud-sd), 0 4px 0 var(--nm-aud-sd);
}
.nm-aud .fader-lbl {
font-size: 8px;
letter-spacing: 1.5px;
text-transform: uppercase;
color: var(--nm-aud-txt);
}
.nm-aud .fader-val {
font-size: 10px;
font-weight: 700;
color: var(--nm-aud-acc);
}
/* Mini knobs */
.nm-aud .knobs-row {
display: flex;
gap: 18px;
align-items: center;
justify-content: center;
padding-top: 8px;
border-top: 1px solid rgba(160, 140, 110, 0.2);
flex-wrap: wrap;
}
.nm-aud .mini-knob-group {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.nm-aud .mini-knob {
width: 56px;
height: 56px;
border-radius: 50%;
background: var(--nm-aud-bg);
box-shadow: 9px 9px 20px var(--nm-aud-sd), -9px -9px 20px var(--nm-aud-sl);
position: relative;
cursor: grab;
}
.nm-aud .mini-knob:active { cursor: grabbing; }
.nm-aud .mini-knob-inner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--nm-aud-ib);
box-shadow: inset 3px 3px 7px var(--nm-aud-sd), inset -3px -3px 7px var(--nm-aud-sl);
}
.nm-aud .mini-knob-dot {
position: absolute;
top: 6px;
left: 50%;
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--nm-aud-acc);
transform: translateX(-50%);
transform-origin: 2.5px 22px;
box-shadow: 0 0 5px var(--nm-aud-acc);
}
.nm-aud .mini-val {
font-size: 11px;
font-weight: 700;
color: var(--nm-aud-txt2);
font-family: 'Space Mono', monospace;
}
@media (max-width: 760px) {
.nm-aud .card { padding: 32px 22px; }
.nm-aud .main-controls { grid-template-columns: 1fr; }
.nm-aud .eq-wrap { justify-content: center; }
.nm-aud .play-controls { justify-content: center; }
} /* ─── 05 Audio Control Suite — warm parchment audio console ───────── */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,500;0,700;1,400&family=Space+Mono:wght@400;700&display=swap');
.nm-aud {
--nm-aud-bg: #f0e6d3;
--nm-aud-sd: #d0c0a0;
--nm-aud-sl: #fffaf0;
--nm-aud-ib: #e8ddc8;
--nm-aud-acc: #c8783a;
--nm-aud-ac2: #e0a858;
--nm-aud-ac3: #8a4a20;
--nm-aud-txt: #7a6a58;
--nm-aud-txt2:#2a1e10;
position: relative;
width: 100%;
min-height: 760px;
background: var(--nm-aud-bg);
font-family: 'Space Mono', ui-monospace, monospace;
color: var(--nm-aud-txt);
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
overflow: hidden;
box-sizing: border-box;
}
.nm-aud *,
.nm-aud *::before,
.nm-aud *::after { box-sizing: border-box; }
.nm-aud .card {
position: relative;
width: 100%;
max-width: 860px;
background: var(--nm-aud-bg);
border-radius: 44px;
padding: 44px 40px 38px;
box-shadow: 24px 24px 56px var(--nm-aud-sd), -24px -24px 56px var(--nm-aud-sl);
display: flex;
flex-direction: column;
gap: 30px;
}
/* Track header */
.nm-aud .track-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.nm-aud .track-title {
font-family: 'Playfair Display', Georgia, serif;
font-size: 26px;
font-weight: 700;
color: var(--nm-aud-txt2);
letter-spacing: -0.5px;
}
.nm-aud .track-sub {
font-size: 11px;
color: var(--nm-aud-txt);
margin-top: 4px;
letter-spacing: 0.5px;
}
.nm-aud .track-bpm {
background: var(--nm-aud-bg);
border-radius: 12px;
padding: 10px 18px;
box-shadow: inset 5px 5px 10px var(--nm-aud-sd), inset -5px -5px 10px var(--nm-aud-sl);
font-size: 11px;
color: var(--nm-aud-txt);
}
.nm-aud .track-bpm span {
color: var(--nm-aud-acc);
font-weight: 700;
font-size: 15px;
}
/* Waveform */
.nm-aud .waveform-row {
display: flex;
align-items: center;
gap: 3px;
height: 36px;
margin-bottom: 6px;
}
.nm-aud .wf-bar {
flex: 1;
border-radius: 2px;
background: linear-gradient(to top, var(--nm-aud-sd), var(--nm-aud-ib));
opacity: 0.45;
min-height: 3px;
}
.nm-aud .wf-bar.played {
background: linear-gradient(to top, var(--nm-aud-acc), var(--nm-aud-ac2));
opacity: 0.7;
}
/* Scrubber */
.nm-aud .scrubber-wrap { position: relative; }
.nm-aud .scrubber-label-row {
display: flex;
justify-content: space-between;
font-size: 10px;
color: var(--nm-aud-txt);
margin-bottom: 10px;
letter-spacing: 0.5px;
}
.nm-aud .scrubber-track {
width: 100%;
height: 10px;
border-radius: 5px;
background: var(--nm-aud-ib);
box-shadow: inset 5px 5px 10px var(--nm-aud-sd), inset -5px -5px 10px var(--nm-aud-sl);
position: relative;
cursor: pointer;
}
.nm-aud .scrubber-fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 38%;
border-radius: 5px;
background: linear-gradient(90deg, var(--nm-aud-ac3), var(--nm-aud-acc), var(--nm-aud-ac2));
pointer-events: none;
}
.nm-aud .scrubber-glow {
position: absolute;
top: -4px;
left: 0;
height: 18px;
width: 38%;
border-radius: 9px;
background: linear-gradient(90deg, transparent, rgba(200, 120, 58, 0.3));
filter: blur(6px);
pointer-events: none;
}
.nm-aud .scrubber {
position: absolute;
top: -4px;
left: 0;
width: 100%;
height: 18px;
opacity: 0;
cursor: pointer;
margin: 0;
z-index: 10;
}
.nm-aud .scrubber-thumb {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 26px;
height: 26px;
border-radius: 50%;
background: var(--nm-aud-bg);
box-shadow: 5px 5px 12px var(--nm-aud-sd), -5px -5px 12px var(--nm-aud-sl);
left: 38%;
pointer-events: none;
transition: box-shadow 0.15s;
}
.nm-aud .scrubber-thumb::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
border-radius: 50%;
background: linear-gradient(145deg, var(--nm-aud-acc), var(--nm-aud-ac2));
box-shadow: 0 0 6px rgba(200, 120, 58, 0.5);
}
/* Main controls grid */
.nm-aud .main-controls {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 28px;
align-items: center;
}
/* Play controls */
.nm-aud .play-controls {
display: flex;
gap: 12px;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
}
.nm-aud .ctrl {
background: var(--nm-aud-bg);
border: none;
cursor: pointer;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: 7px 7px 15px var(--nm-aud-sd), -7px -7px 15px var(--nm-aud-sl);
transition: all 0.15s ease;
color: var(--nm-aud-txt2);
font-size: 16px;
}
.nm-aud .ctrl:active {
box-shadow: inset 4px 4px 10px var(--nm-aud-sd), inset -4px -4px 10px var(--nm-aud-sl);
}
.nm-aud .ctrl:hover { transform: scale(1.05); }
.nm-aud .ctrl-sm { width: 42px; height: 42px; }
.nm-aud .ctrl-md { width: 52px; height: 52px; font-size: 18px; }
.nm-aud .ctrl-play {
width: 66px;
height: 66px;
font-size: 22px;
background: linear-gradient(145deg, var(--nm-aud-acc), var(--nm-aud-ac3));
color: #fffaf0;
box-shadow: 10px 10px 22px rgba(200, 120, 58, 0.4), -6px -6px 16px var(--nm-aud-sl);
}
.nm-aud .ctrl-play:hover { transform: scale(1.05); }
.nm-aud .ctrl-play:active {
box-shadow: inset 6px 6px 14px rgba(0, 0, 0, 0.2);
transform: scale(0.97);
}
/* Volume knob */
.nm-aud .vol-knob-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.nm-aud .vol-knob {
width: 110px;
height: 110px;
border-radius: 50%;
background: var(--nm-aud-bg);
box-shadow: 14px 14px 32px var(--nm-aud-sd), -14px -14px 32px var(--nm-aud-sl);
position: relative;
cursor: grab;
user-select: none;
}
.nm-aud .vol-knob:active { cursor: grabbing; }
.nm-aud .vol-knob-inner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 70px;
height: 70px;
border-radius: 50%;
background: var(--nm-aud-ib);
box-shadow: inset 6px 6px 14px var(--nm-aud-sd), inset -6px -6px 14px var(--nm-aud-sl);
}
.nm-aud .vol-knob-dot {
position: absolute;
top: 12px;
left: 50%;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--nm-aud-acc);
transform: translateX(-50%);
transform-origin: 3px 43px;
box-shadow: 0 0 8px var(--nm-aud-acc);
transition: transform 0.05s;
}
.nm-aud .vol-tick-svg { position: absolute; inset: 0; }
.nm-aud .mini-lbl {
font-size: 9px;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--nm-aud-txt);
}
.nm-aud .vol-val {
font-size: 20px;
font-weight: 700;
color: var(--nm-aud-txt2);
margin-top: -6px;
font-family: 'Space Mono', monospace;
}
/* EQ faders */
.nm-aud .eq-wrap {
display: flex;
gap: 14px;
align-items: flex-end;
justify-content: flex-end;
}
.nm-aud .fader-group {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.nm-aud .fader {
position: relative;
width: 28px;
height: 110px;
}
.nm-aud .fader-track {
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 8px;
height: 100%;
border-radius: 4px;
background: var(--nm-aud-ib);
box-shadow: inset 3px 3px 7px var(--nm-aud-sd), inset -3px -3px 7px var(--nm-aud-sl);
}
.nm-aud .fader-fill {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 8px;
bottom: 0;
border-radius: 4px;
background: linear-gradient(to top, var(--nm-aud-ac3), var(--nm-aud-acc), var(--nm-aud-ac2));
}
.nm-aud .fader-thumb {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 28px;
height: 20px;
border-radius: 6px;
background: var(--nm-aud-bg);
box-shadow: 4px 4px 8px var(--nm-aud-sd), -4px -4px 8px var(--nm-aud-sl);
cursor: ns-resize;
display: flex;
align-items: center;
justify-content: center;
}
.nm-aud .fader-thumb::before {
content: '';
display: block;
width: 14px;
height: 2px;
border-radius: 1px;
background: var(--nm-aud-sd);
box-shadow: 0 -4px 0 var(--nm-aud-sd), 0 4px 0 var(--nm-aud-sd);
}
.nm-aud .fader-lbl {
font-size: 8px;
letter-spacing: 1.5px;
text-transform: uppercase;
color: var(--nm-aud-txt);
}
.nm-aud .fader-val {
font-size: 10px;
font-weight: 700;
color: var(--nm-aud-acc);
}
/* Mini knobs */
.nm-aud .knobs-row {
display: flex;
gap: 18px;
align-items: center;
justify-content: center;
padding-top: 8px;
border-top: 1px solid rgba(160, 140, 110, 0.2);
flex-wrap: wrap;
}
.nm-aud .mini-knob-group {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.nm-aud .mini-knob {
width: 56px;
height: 56px;
border-radius: 50%;
background: var(--nm-aud-bg);
box-shadow: 9px 9px 20px var(--nm-aud-sd), -9px -9px 20px var(--nm-aud-sl);
position: relative;
cursor: grab;
}
.nm-aud .mini-knob:active { cursor: grabbing; }
.nm-aud .mini-knob-inner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--nm-aud-ib);
box-shadow: inset 3px 3px 7px var(--nm-aud-sd), inset -3px -3px 7px var(--nm-aud-sl);
}
.nm-aud .mini-knob-dot {
position: absolute;
top: 6px;
left: 50%;
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--nm-aud-acc);
transform: translateX(-50%);
transform-origin: 2.5px 22px;
box-shadow: 0 0 5px var(--nm-aud-acc);
}
.nm-aud .mini-val {
font-size: 11px;
font-weight: 700;
color: var(--nm-aud-txt2);
font-family: 'Space Mono', monospace;
}
@media (max-width: 760px) {
.nm-aud .card { padding: 32px 22px; }
.nm-aud .main-controls { grid-template-columns: 1fr; }
.nm-aud .eq-wrap { justify-content: center; }
.nm-aud .play-controls { justify-content: center; }
}(() => {
const root = document.querySelector('.nm-aud');
if (!root) return;
// ─── Waveform ───
const waveRow = root.querySelector('[data-nm-aud-wave]');
const waveHeights = [6,10,18,14,24,20,32,28,36,30,40,34,42,38,48,52,44,50,40,46,38,42,32,36,28,30,22,26,18,20,14,16,10,12,8,10,14,18,22,28,32,36,30,24,20,16,12,8,6];
let scrubberPct = 0.38;
waveHeights.forEach((h, i) => {
const bar = document.createElement('div');
bar.className = 'wf-bar' + (i < waveHeights.length * scrubberPct ? ' played' : '');
bar.style.height = Math.max(3, h * 0.7) + 'px';
waveRow.appendChild(bar);
});
// ─── Scrubber ───
const input = root.querySelector('[data-nm-aud-scrub]');
const fill = root.querySelector('[data-nm-aud-fill]');
const glow = root.querySelector('[data-nm-aud-glow]');
const thumb = root.querySelector('[data-nm-aud-thumb]');
const timeNow = root.querySelector('[data-nm-aud-time-now]');
const totalSec = 227;
function updateScrubber(val) {
const pct = val / 100;
fill.style.width = (pct * 100) + '%';
glow.style.width = (pct * 100) + '%';
thumb.style.left = (pct * 100) + '%';
const sec = Math.round(pct * totalSec);
timeNow.textContent = Math.floor(sec / 60) + ':' + String(sec % 60).padStart(2, '0');
waveRow.querySelectorAll('.wf-bar').forEach((b, i) => {
b.classList.toggle('played', i < waveHeights.length * pct);
});
}
input.addEventListener('input', () => updateScrubber(+input.value));
// ─── Volume Knob ───
const volKnob = root.querySelector('[data-nm-aud-volknob]');
const volDot = root.querySelector('[data-nm-aud-voldot]');
const volVal = root.querySelector('[data-nm-aud-volval]');
const volTicks = root.querySelector('[data-nm-aud-volticks]');
let volAngle = -45;
let volDragging = false, volStartY = 0, volStartAngle = -45;
// Tick marks
const svgNS = 'http://www.w3.org/2000/svg';
for (let t = 0; t <= 20; t++) {
const frac = t / 20;
const ang = -225 + frac * 270;
const rad = ang * Math.PI / 180;
const r1 = 54, r2 = t % 5 === 0 ? 48 : 51;
const line = document.createElementNS(svgNS, 'line');
line.setAttribute('x1', String(60 + r1 * Math.cos(rad)));
line.setAttribute('y1', String(60 + r1 * Math.sin(rad)));
line.setAttribute('x2', String(60 + r2 * Math.cos(rad)));
line.setAttribute('y2', String(60 + r2 * Math.sin(rad)));
line.setAttribute('stroke', frac < 0.72 ? '#c8783a' : '#d0c0a0');
line.setAttribute('stroke-width', t % 5 === 0 ? '2' : '1');
line.setAttribute('stroke-linecap', 'round');
line.setAttribute('opacity', frac < 0.72 ? '0.8' : '0.4');
volTicks.appendChild(line);
}
function setVol(angle) {
volAngle = Math.max(-140, Math.min(140, angle));
volDot.style.transform = 'translateX(-50%) rotate(' + volAngle + 'deg)';
const pct = Math.round((volAngle + 140) / 280 * 100);
volVal.textContent = pct;
}
setVol(-45);
volKnob.addEventListener('mousedown', e => {
volDragging = true; volStartY = e.clientY; volStartAngle = volAngle;
document.body.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!volDragging) return;
setVol(volStartAngle - (e.clientY - volStartY) * 1.2);
});
document.addEventListener('mouseup', () => {
if (volDragging) { volDragging = false; document.body.style.cursor = ''; }
});
// ─── EQ Faders ───
const eqData = [
{ lbl: '32Hz', val: 68 },
{ lbl: '250Hz', val: 55 },
{ lbl: '1kHz', val: 80 },
{ lbl: '4kHz', val: 62 },
{ lbl: '16kHz', val: 44 },
];
const eqWrap = root.querySelector('[data-nm-aud-eq]');
eqData.forEach(({ lbl, val }) => {
const h = 110;
const fillH = Math.round(val / 100 * h);
const g = document.createElement('div');
g.className = 'fader-group';
g.innerHTML =
'<div class="fader-val">' + (val >= 50 ? '+' : '') + (val - 50) + '</div>' +
'<div class="fader">' +
' <div class="fader-track"></div>' +
' <div class="fader-fill" style="height:' + fillH + 'px"></div>' +
' <div class="fader-thumb" style="bottom:' + (fillH - 10) + 'px"></div>' +
'</div>' +
'<div class="fader-lbl">' + lbl + '</div>';
eqWrap.appendChild(g);
const thumb2 = g.querySelector('.fader-thumb');
const fill2 = g.querySelector('.fader-fill');
const valEl = g.querySelector('.fader-val');
let dragging = false, startY2 = 0, startPct = val / 100;
thumb2.addEventListener('mousedown', e => {
dragging = true; startY2 = e.clientY;
startPct = parseInt(fill2.style.height, 10) / h;
document.body.style.cursor = 'ns-resize';
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!dragging) return;
const delta = (startY2 - e.clientY) / h;
const pct = Math.max(0, Math.min(1, startPct + delta));
const fh = Math.round(pct * h);
fill2.style.height = fh + 'px';
thumb2.style.bottom = (fh - 10) + 'px';
const v = Math.round(pct * 100);
valEl.textContent = (v >= 50 ? '+' : '') + (v - 50);
});
document.addEventListener('mouseup', () => {
if (dragging) { dragging = false; document.body.style.cursor = ''; }
});
});
// ─── Mini Knobs ───
const knobData = [
{ lbl: 'Bass', val: -20 },
{ lbl: 'Treble', val: 30 },
{ lbl: 'Reverb', val: -5 },
{ lbl: 'Delay', val: 15 },
{ lbl: 'Width', val: 50 },
{ lbl: 'Drive', val: -30 },
];
const knobsRow = root.querySelector('[data-nm-aud-knobs]');
knobData.forEach(({ lbl, val }) => {
const g = document.createElement('div');
g.className = 'mini-knob-group';
g.innerHTML =
'<div class="mini-knob">' +
' <div class="mini-knob-inner"></div>' +
' <div class="mini-knob-dot"></div>' +
'</div>' +
'<div class="mini-lbl">' + lbl + '</div>' +
'<div class="mini-val">' + (val > 0 ? '+' : '') + val + '</div>';
knobsRow.appendChild(g);
const dot = g.querySelector('.mini-knob-dot');
const valEl2 = g.querySelector('.mini-val');
let angle = val;
dot.style.transform = 'translateX(-50%) rotate(' + angle + 'deg)';
let drag = false, sy = 0, sa = angle;
g.querySelector('.mini-knob').addEventListener('mousedown', e => {
drag = true; sy = e.clientY; sa = angle;
document.body.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!drag) return;
angle = Math.max(-140, Math.min(140, sa - (e.clientY - sy) * 1.5));
dot.style.transform = 'translateX(-50%) rotate(' + angle + 'deg)';
const v = Math.round(angle);
valEl2.textContent = (v > 0 ? '+' : '') + v;
});
document.addEventListener('mouseup', () => {
if (drag) { drag = false; document.body.style.cursor = ''; }
});
});
// ─── Play toggle ───
let playing = false;
const playBtn = root.querySelector('[data-nm-aud-play]');
playBtn.addEventListener('click', () => {
playing = !playing;
playBtn.textContent = playing ? '⏸' : '▶';
});
})(); (() => {
const root = document.querySelector('.nm-aud');
if (!root) return;
// ─── Waveform ───
const waveRow = root.querySelector('[data-nm-aud-wave]');
const waveHeights = [6,10,18,14,24,20,32,28,36,30,40,34,42,38,48,52,44,50,40,46,38,42,32,36,28,30,22,26,18,20,14,16,10,12,8,10,14,18,22,28,32,36,30,24,20,16,12,8,6];
let scrubberPct = 0.38;
waveHeights.forEach((h, i) => {
const bar = document.createElement('div');
bar.className = 'wf-bar' + (i < waveHeights.length * scrubberPct ? ' played' : '');
bar.style.height = Math.max(3, h * 0.7) + 'px';
waveRow.appendChild(bar);
});
// ─── Scrubber ───
const input = root.querySelector('[data-nm-aud-scrub]');
const fill = root.querySelector('[data-nm-aud-fill]');
const glow = root.querySelector('[data-nm-aud-glow]');
const thumb = root.querySelector('[data-nm-aud-thumb]');
const timeNow = root.querySelector('[data-nm-aud-time-now]');
const totalSec = 227;
function updateScrubber(val) {
const pct = val / 100;
fill.style.width = (pct * 100) + '%';
glow.style.width = (pct * 100) + '%';
thumb.style.left = (pct * 100) + '%';
const sec = Math.round(pct * totalSec);
timeNow.textContent = Math.floor(sec / 60) + ':' + String(sec % 60).padStart(2, '0');
waveRow.querySelectorAll('.wf-bar').forEach((b, i) => {
b.classList.toggle('played', i < waveHeights.length * pct);
});
}
input.addEventListener('input', () => updateScrubber(+input.value));
// ─── Volume Knob ───
const volKnob = root.querySelector('[data-nm-aud-volknob]');
const volDot = root.querySelector('[data-nm-aud-voldot]');
const volVal = root.querySelector('[data-nm-aud-volval]');
const volTicks = root.querySelector('[data-nm-aud-volticks]');
let volAngle = -45;
let volDragging = false, volStartY = 0, volStartAngle = -45;
// Tick marks
const svgNS = 'http://www.w3.org/2000/svg';
for (let t = 0; t <= 20; t++) {
const frac = t / 20;
const ang = -225 + frac * 270;
const rad = ang * Math.PI / 180;
const r1 = 54, r2 = t % 5 === 0 ? 48 : 51;
const line = document.createElementNS(svgNS, 'line');
line.setAttribute('x1', String(60 + r1 * Math.cos(rad)));
line.setAttribute('y1', String(60 + r1 * Math.sin(rad)));
line.setAttribute('x2', String(60 + r2 * Math.cos(rad)));
line.setAttribute('y2', String(60 + r2 * Math.sin(rad)));
line.setAttribute('stroke', frac < 0.72 ? '#c8783a' : '#d0c0a0');
line.setAttribute('stroke-width', t % 5 === 0 ? '2' : '1');
line.setAttribute('stroke-linecap', 'round');
line.setAttribute('opacity', frac < 0.72 ? '0.8' : '0.4');
volTicks.appendChild(line);
}
function setVol(angle) {
volAngle = Math.max(-140, Math.min(140, angle));
volDot.style.transform = 'translateX(-50%) rotate(' + volAngle + 'deg)';
const pct = Math.round((volAngle + 140) / 280 * 100);
volVal.textContent = pct;
}
setVol(-45);
volKnob.addEventListener('mousedown', e => {
volDragging = true; volStartY = e.clientY; volStartAngle = volAngle;
document.body.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!volDragging) return;
setVol(volStartAngle - (e.clientY - volStartY) * 1.2);
});
document.addEventListener('mouseup', () => {
if (volDragging) { volDragging = false; document.body.style.cursor = ''; }
});
// ─── EQ Faders ───
const eqData = [
{ lbl: '32Hz', val: 68 },
{ lbl: '250Hz', val: 55 },
{ lbl: '1kHz', val: 80 },
{ lbl: '4kHz', val: 62 },
{ lbl: '16kHz', val: 44 },
];
const eqWrap = root.querySelector('[data-nm-aud-eq]');
eqData.forEach(({ lbl, val }) => {
const h = 110;
const fillH = Math.round(val / 100 * h);
const g = document.createElement('div');
g.className = 'fader-group';
g.innerHTML =
'<div class="fader-val">' + (val >= 50 ? '+' : '') + (val - 50) + '</div>' +
'<div class="fader">' +
' <div class="fader-track"></div>' +
' <div class="fader-fill" style="height:' + fillH + 'px"></div>' +
' <div class="fader-thumb" style="bottom:' + (fillH - 10) + 'px"></div>' +
'</div>' +
'<div class="fader-lbl">' + lbl + '</div>';
eqWrap.appendChild(g);
const thumb2 = g.querySelector('.fader-thumb');
const fill2 = g.querySelector('.fader-fill');
const valEl = g.querySelector('.fader-val');
let dragging = false, startY2 = 0, startPct = val / 100;
thumb2.addEventListener('mousedown', e => {
dragging = true; startY2 = e.clientY;
startPct = parseInt(fill2.style.height, 10) / h;
document.body.style.cursor = 'ns-resize';
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!dragging) return;
const delta = (startY2 - e.clientY) / h;
const pct = Math.max(0, Math.min(1, startPct + delta));
const fh = Math.round(pct * h);
fill2.style.height = fh + 'px';
thumb2.style.bottom = (fh - 10) + 'px';
const v = Math.round(pct * 100);
valEl.textContent = (v >= 50 ? '+' : '') + (v - 50);
});
document.addEventListener('mouseup', () => {
if (dragging) { dragging = false; document.body.style.cursor = ''; }
});
});
// ─── Mini Knobs ───
const knobData = [
{ lbl: 'Bass', val: -20 },
{ lbl: 'Treble', val: 30 },
{ lbl: 'Reverb', val: -5 },
{ lbl: 'Delay', val: 15 },
{ lbl: 'Width', val: 50 },
{ lbl: 'Drive', val: -30 },
];
const knobsRow = root.querySelector('[data-nm-aud-knobs]');
knobData.forEach(({ lbl, val }) => {
const g = document.createElement('div');
g.className = 'mini-knob-group';
g.innerHTML =
'<div class="mini-knob">' +
' <div class="mini-knob-inner"></div>' +
' <div class="mini-knob-dot"></div>' +
'</div>' +
'<div class="mini-lbl">' + lbl + '</div>' +
'<div class="mini-val">' + (val > 0 ? '+' : '') + val + '</div>';
knobsRow.appendChild(g);
const dot = g.querySelector('.mini-knob-dot');
const valEl2 = g.querySelector('.mini-val');
let angle = val;
dot.style.transform = 'translateX(-50%) rotate(' + angle + 'deg)';
let drag = false, sy = 0, sa = angle;
g.querySelector('.mini-knob').addEventListener('mousedown', e => {
drag = true; sy = e.clientY; sa = angle;
document.body.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!drag) return;
angle = Math.max(-140, Math.min(140, sa - (e.clientY - sy) * 1.5));
dot.style.transform = 'translateX(-50%) rotate(' + angle + 'deg)';
const v = Math.round(angle);
valEl2.textContent = (v > 0 ? '+' : '') + v;
});
document.addEventListener('mouseup', () => {
if (drag) { drag = false; document.body.style.cursor = ''; }
});
});
// ─── Play toggle ───
let playing = false;
const playBtn = root.querySelector('[data-nm-aud-play]');
playBtn.addEventListener('click', () => {
playing = !playing;
playBtn.textContent = playing ? '⏸' : '▶';
});
})();