Vinyl Player
A deep-slate dark-neumorphic music player with a spinning vinyl disc featuring real grooves via repeating-radial-gradient. Includes an 18-bar animated EQ visualizer synced to playback state, arc progress scrubber with glowing gradient thumb, tonearm SVG, amber accent label, and a full transport control set (shuffle, prev, play/pause, next, loop). DM Serif Display for warm sophistication. Best for music apps, podcast platforms, audio dashboards.
Published
The code
<section class="nm-vin" aria-label="Vinyl player widget">
<div class="card">
<span class="glow" aria-hidden="true"></span>
<span class="glow2" aria-hidden="true"></span>
<div class="disc-wrap">
<div class="disc-outer">
<div class="disc" id="nm-vin-disc">
<div class="disc-label" aria-hidden="true">♪</div>
</div>
<div class="tonearm-wrap" aria-hidden="true">
<svg width="70" height="80" viewBox="0 0 70 80">
<line x1="10" y1="10" x2="50" y2="65" stroke="#888" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="10" cy="10" r="6" fill="#444" stroke="#666" stroke-width="1"/>
<circle cx="50" cy="65" r="4" fill="#e0a86c" opacity="0.9"/>
</svg>
</div>
</div>
</div>
<div class="track-info">
<div class="track-title">Midnight Reverie</div>
<div class="track-artist">Aurelia Voss · Late Hours</div>
</div>
<div class="progress-wrap">
<div class="progress-track">
<div class="progress-fill" id="nm-vin-fill">
<span class="progress-thumb" aria-hidden="true"></span>
</div>
</div>
<div class="progress-times">
<span id="nm-vin-elapsed">1:28</span>
<span>3:52</span>
</div>
</div>
<div class="eq-wrap paused" id="nm-vin-eq" aria-hidden="true">
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
</div>
<div class="controls">
<button type="button" class="ctrl-btn sm" aria-label="Shuffle">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/>
<polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Previous">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="19,20 9,12 19,4"/><rect x="5" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn lg" id="nm-vin-play" aria-label="Play / Pause">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="6,4 20,12 6,20"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Next">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="5,4 15,12 5,20"/><rect x="17" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Loop">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/>
<polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/>
</svg>
</button>
</div>
<div class="volume-row">
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
</svg>
</span>
<div class="vol-track"><div class="vol-fill"></div></div>
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
</span>
</div>
</div>
</section><section class="nm-vin" aria-label="Vinyl player widget">
<div class="card">
<span class="glow" aria-hidden="true"></span>
<span class="glow2" aria-hidden="true"></span>
<div class="disc-wrap">
<div class="disc-outer">
<div class="disc" id="nm-vin-disc">
<div class="disc-label" aria-hidden="true">♪</div>
</div>
<div class="tonearm-wrap" aria-hidden="true">
<svg width="70" height="80" viewBox="0 0 70 80">
<line x1="10" y1="10" x2="50" y2="65" stroke="#888" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="10" cy="10" r="6" fill="#444" stroke="#666" stroke-width="1"/>
<circle cx="50" cy="65" r="4" fill="#e0a86c" opacity="0.9"/>
</svg>
</div>
</div>
</div>
<div class="track-info">
<div class="track-title">Midnight Reverie</div>
<div class="track-artist">Aurelia Voss · Late Hours</div>
</div>
<div class="progress-wrap">
<div class="progress-track">
<div class="progress-fill" id="nm-vin-fill">
<span class="progress-thumb" aria-hidden="true"></span>
</div>
</div>
<div class="progress-times">
<span id="nm-vin-elapsed">1:28</span>
<span>3:52</span>
</div>
</div>
<div class="eq-wrap paused" id="nm-vin-eq" aria-hidden="true">
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
</div>
<div class="controls">
<button type="button" class="ctrl-btn sm" aria-label="Shuffle">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/>
<polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Previous">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="19,20 9,12 19,4"/><rect x="5" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn lg" id="nm-vin-play" aria-label="Play / Pause">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="6,4 20,12 6,20"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Next">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="5,4 15,12 5,20"/><rect x="17" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Loop">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/>
<polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/>
</svg>
</button>
</div>
<div class="volume-row">
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
</svg>
</span>
<div class="vol-track"><div class="vol-fill"></div></div>
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
</span>
</div>
</div>
</section>/* ─── 02 Vinyl Player — dark-slate dark-neumorphic music widget ───── */
@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@300;400&display=swap');
.nm-vin {
--nm-vin-bg: #1e2230;
--nm-vin-neu-dark: #141720;
--nm-vin-neu-light: #282e40;
--nm-vin-text-primary: #d0d4e0;
--nm-vin-text-muted: #606880;
--nm-vin-accent: #e0a86c;
--nm-vin-accent2: #7c6cdb;
--nm-vin-inset: #1b1f2d;
position: relative;
width: 100%;
min-height: 720px;
background: var(--nm-vin-bg);
font-family: 'DM Mono', ui-monospace, monospace;
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
overflow: hidden;
box-sizing: border-box;
}
.nm-vin *,
.nm-vin *::before,
.nm-vin *::after {
box-sizing: border-box;
}
/* The inner card holds the dark neumorphic shadow */
.nm-vin .card {
position: relative;
width: 100%;
max-width: 400px;
background: var(--nm-vin-bg);
border-radius: 36px;
padding: 40px 34px 36px;
box-shadow: 14px 14px 32px var(--nm-vin-neu-dark), -10px -10px 28px var(--nm-vin-neu-light);
overflow: hidden;
}
/* Glows — contained inside the card */
.nm-vin .glow {
position: absolute;
width: 200px;
height: 200px;
border-radius: 50%;
background: radial-gradient(circle, rgba(124, 108, 219, 0.08), transparent 70%);
top: -60px;
right: -40px;
pointer-events: none;
}
.nm-vin .glow2 {
position: absolute;
width: 160px;
height: 160px;
border-radius: 50%;
background: radial-gradient(circle, rgba(224, 168, 108, 0.07), transparent 70%);
bottom: -40px;
left: -30px;
pointer-events: none;
}
/* Vinyl disc */
.nm-vin .disc-wrap {
display: flex;
justify-content: center;
position: relative;
}
.nm-vin .disc-outer {
width: 200px;
height: 200px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow: 10px 10px 24px var(--nm-vin-neu-dark), -8px -8px 20px var(--nm-vin-neu-light);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.nm-vin .disc {
width: 170px;
height: 170px;
border-radius: 50%;
background: repeating-radial-gradient(circle at 50% 50%,
transparent 0px, transparent 3px,
rgba(255, 255, 255, 0.02) 3px, rgba(255, 255, 255, 0.02) 4px
), conic-gradient(from 0deg,
#1a1d2a 0deg, #242838 90deg, #1a1d2a 180deg, #242838 270deg, #1a1d2a 360deg
);
box-shadow: inset 4px 4px 12px rgba(0, 0, 0, 0.6), inset -3px -3px 8px rgba(255, 255, 255, 0.04);
display: flex;
align-items: center;
justify-content: center;
animation: nm-vin-spin 4s linear infinite;
animation-play-state: paused;
}
.nm-vin .disc.playing {
animation-play-state: running;
}
@keyframes nm-vin-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.nm-vin .disc-label {
width: 62px;
height: 62px;
border-radius: 50%;
background: linear-gradient(135deg, var(--nm-vin-accent), #c4863a);
box-shadow: inset 2px 2px 6px rgba(0, 0, 0, 0.3), inset -1px -1px 3px rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: #1a1d2a;
}
/* Tonearm */
.nm-vin .tonearm-wrap {
position: absolute;
top: -8px;
right: -8px;
width: 70px;
height: 80px;
}
.nm-vin .tonearm-wrap svg {
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.6));
}
/* Track info */
.nm-vin .track-info {
text-align: center;
margin-bottom: 22px;
}
.nm-vin .track-title {
font-family: 'DM Serif Display', Georgia, serif;
font-size: 22px;
color: var(--nm-vin-text-primary);
margin-bottom: 5px;
letter-spacing: -0.3px;
}
.nm-vin .track-artist {
font-size: 10px;
font-weight: 300;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--nm-vin-text-muted);
}
/* Progress bar */
.nm-vin .progress-wrap {
margin-bottom: 22px;
position: relative;
}
.nm-vin .progress-track {
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow: inset 2px 2px 4px var(--nm-vin-neu-dark), inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
cursor: pointer;
}
.nm-vin .progress-fill {
height: 100%;
width: 38%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
position: relative;
}
.nm-vin .progress-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow: 2px 2px 6px var(--nm-vin-neu-dark), -1px -1px 4px var(--nm-vin-neu-light);
position: absolute;
right: -6px;
top: -4px;
}
.nm-vin .progress-times {
display: flex;
justify-content: space-between;
margin-top: 8px;
}
.nm-vin .progress-times span {
font-size: 9px;
color: var(--nm-vin-text-muted);
letter-spacing: 1px;
}
/* EQ bars */
.nm-vin .eq-wrap {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4px;
height: 30px;
margin-bottom: 24px;
}
.nm-vin .eq-bar {
display: block;
width: 4px;
border-radius: 2px;
background: linear-gradient(to top, var(--nm-vin-accent2), var(--nm-vin-accent));
animation: nm-vin-eq 0.6s ease-in-out infinite alternate;
opacity: 0.7;
}
.nm-vin .eq-bar:nth-child(1) {
height: 40%;
animation-duration: 0.5s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(2) {
height: 70%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(3) {
height: 90%;
animation-duration: 0.4s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(4) {
height: 60%;
animation-duration: 0.6s;
animation-delay: 0.05s;
}
.nm-vin .eq-bar:nth-child(5) {
height: 100%;
animation-duration: 0.8s;
animation-delay: 0.15s;
}
.nm-vin .eq-bar:nth-child(6) {
height: 75%;
animation-duration: 0.5s;
animation-delay: 0.25s;
}
.nm-vin .eq-bar:nth-child(7) {
height: 50%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(8) {
height: 85%;
animation-duration: 0.4s;
animation-delay: 0.3s;
}
.nm-vin .eq-bar:nth-child(9) {
height: 60%;
animation-duration: 0.6s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(10) {
height: 35%;
animation-duration: 0.5s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(11) {
height: 80%;
animation-duration: 0.9s;
animation-delay: 0.05s;
}
.nm-vin .eq-bar:nth-child(12) {
height: 55%;
animation-duration: 0.6s;
animation-delay: 0.15s;
}
.nm-vin .eq-bar:nth-child(13) {
height: 45%;
animation-duration: 0.4s;
animation-delay: 0.25s;
}
.nm-vin .eq-bar:nth-child(14) {
height: 70%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(15) {
height: 90%;
animation-duration: 0.5s;
animation-delay: 0.35s;
}
.nm-vin .eq-bar:nth-child(16) {
height: 65%;
animation-duration: 0.8s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(17) {
height: 40%;
animation-duration: 0.6s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(18) {
height: 55%;
animation-duration: 0.4s;
animation-delay: 0.1s;
}
@keyframes nm-vin-eq {
0% {
transform: scaleY(0.3);
}
100% {
transform: scaleY(1);
}
}
.nm-vin .eq-wrap.paused .eq-bar {
animation-play-state: paused;
}
/* Controls */
.nm-vin .controls {
display: flex;
align-items: center;
justify-content: center;
gap: 18px;
}
.nm-vin .ctrl-btn {
border: none;
background: var(--nm-vin-bg);
border-radius: 50%;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--nm-vin-text-muted);
transition: all 0.15s;
}
.nm-vin .ctrl-btn.sm {
width: 40px;
height: 40px;
box-shadow: 4px 4px 10px var(--nm-vin-neu-dark), -3px -3px 8px var(--nm-vin-neu-light);
font-size: 12px;
}
.nm-vin .ctrl-btn.sm:active {
box-shadow: inset 2px 2px 6px var(--nm-vin-neu-dark), inset -1px -1px 4px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
}
.nm-vin .ctrl-btn.lg {
width: 62px;
height: 62px;
box-shadow: 7px 7px 16px var(--nm-vin-neu-dark), -6px -6px 14px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
font-size: 20px;
}
.nm-vin .ctrl-btn.lg:active {
box-shadow: inset 3px 3px 8px var(--nm-vin-neu-dark), inset -2px -2px 6px var(--nm-vin-neu-light);
}
/* Volume */
.nm-vin .volume-row {
display: flex;
align-items: center;
gap: 12px;
}
.nm-vin .vol-icon {
color: var(--nm-vin-text-muted);
font-size: 11px;
}
.nm-vin .vol-track {
flex: 1;
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow: inset 2px 2px 4px var(--nm-vin-neu-dark), inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
}
.nm-vin .vol-fill {
height: 100%;
width: 62%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
}
@media (prefers-reduced-motion: reduce) {
.nm-vin .disc,
.nm-vin .eq-bar {
animation: none;
}
}/* ─── 02 Vinyl Player — dark-slate dark-neumorphic music widget ───── */
@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@300;400&display=swap');
.nm-vin {
--nm-vin-bg: #1e2230;
--nm-vin-neu-dark: #141720;
--nm-vin-neu-light: #282e40;
--nm-vin-text-primary: #d0d4e0;
--nm-vin-text-muted: #606880;
--nm-vin-accent: #e0a86c;
--nm-vin-accent2: #7c6cdb;
--nm-vin-inset: #1b1f2d;
position: relative;
width: 100%;
min-height: 720px;
background: var(--nm-vin-bg);
font-family: 'DM Mono', ui-monospace, monospace;
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
overflow: hidden;
box-sizing: border-box;
}
.nm-vin *,
.nm-vin *::before,
.nm-vin *::after {
box-sizing: border-box;
}
/* The inner card holds the dark neumorphic shadow */
.nm-vin .card {
position: relative;
width: 100%;
max-width: 400px;
background: var(--nm-vin-bg);
border-radius: 36px;
padding: 40px 34px 36px;
box-shadow: 14px 14px 32px var(--nm-vin-neu-dark), -10px -10px 28px var(--nm-vin-neu-light);
overflow: hidden;
}
/* Glows — contained inside the card */
.nm-vin .glow {
position: absolute;
width: 200px;
height: 200px;
border-radius: 50%;
background: radial-gradient(circle, rgba(124, 108, 219, 0.08), transparent 70%);
top: -60px;
right: -40px;
pointer-events: none;
}
.nm-vin .glow2 {
position: absolute;
width: 160px;
height: 160px;
border-radius: 50%;
background: radial-gradient(circle, rgba(224, 168, 108, 0.07), transparent 70%);
bottom: -40px;
left: -30px;
pointer-events: none;
}
/* Vinyl disc */
.nm-vin .disc-wrap {
display: flex;
justify-content: center;
position: relative;
}
.nm-vin .disc-outer {
width: 200px;
height: 200px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow: 10px 10px 24px var(--nm-vin-neu-dark), -8px -8px 20px var(--nm-vin-neu-light);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.nm-vin .disc {
width: 170px;
height: 170px;
border-radius: 50%;
background: repeating-radial-gradient(circle at 50% 50%,
transparent 0px, transparent 3px,
rgba(255, 255, 255, 0.02) 3px, rgba(255, 255, 255, 0.02) 4px
), conic-gradient(from 0deg,
#1a1d2a 0deg, #242838 90deg, #1a1d2a 180deg, #242838 270deg, #1a1d2a 360deg
);
box-shadow: inset 4px 4px 12px rgba(0, 0, 0, 0.6), inset -3px -3px 8px rgba(255, 255, 255, 0.04);
display: flex;
align-items: center;
justify-content: center;
animation: nm-vin-spin 4s linear infinite;
animation-play-state: paused;
}
.nm-vin .disc.playing {
animation-play-state: running;
}
@keyframes nm-vin-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.nm-vin .disc-label {
width: 62px;
height: 62px;
border-radius: 50%;
background: linear-gradient(135deg, var(--nm-vin-accent), #c4863a);
box-shadow: inset 2px 2px 6px rgba(0, 0, 0, 0.3), inset -1px -1px 3px rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: #1a1d2a;
}
/* Tonearm */
.nm-vin .tonearm-wrap {
position: absolute;
top: -8px;
right: -8px;
width: 70px;
height: 80px;
}
.nm-vin .tonearm-wrap svg {
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.6));
}
/* Track info */
.nm-vin .track-info {
text-align: center;
margin-bottom: 22px;
}
.nm-vin .track-title {
font-family: 'DM Serif Display', Georgia, serif;
font-size: 22px;
color: var(--nm-vin-text-primary);
margin-bottom: 5px;
letter-spacing: -0.3px;
}
.nm-vin .track-artist {
font-size: 10px;
font-weight: 300;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--nm-vin-text-muted);
}
/* Progress bar */
.nm-vin .progress-wrap {
margin-bottom: 22px;
position: relative;
}
.nm-vin .progress-track {
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow: inset 2px 2px 4px var(--nm-vin-neu-dark), inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
cursor: pointer;
}
.nm-vin .progress-fill {
height: 100%;
width: 38%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
position: relative;
}
.nm-vin .progress-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow: 2px 2px 6px var(--nm-vin-neu-dark), -1px -1px 4px var(--nm-vin-neu-light);
position: absolute;
right: -6px;
top: -4px;
}
.nm-vin .progress-times {
display: flex;
justify-content: space-between;
margin-top: 8px;
}
.nm-vin .progress-times span {
font-size: 9px;
color: var(--nm-vin-text-muted);
letter-spacing: 1px;
}
/* EQ bars */
.nm-vin .eq-wrap {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4px;
height: 30px;
margin-bottom: 24px;
}
.nm-vin .eq-bar {
display: block;
width: 4px;
border-radius: 2px;
background: linear-gradient(to top, var(--nm-vin-accent2), var(--nm-vin-accent));
animation: nm-vin-eq 0.6s ease-in-out infinite alternate;
opacity: 0.7;
}
.nm-vin .eq-bar:nth-child(1) {
height: 40%;
animation-duration: 0.5s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(2) {
height: 70%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(3) {
height: 90%;
animation-duration: 0.4s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(4) {
height: 60%;
animation-duration: 0.6s;
animation-delay: 0.05s;
}
.nm-vin .eq-bar:nth-child(5) {
height: 100%;
animation-duration: 0.8s;
animation-delay: 0.15s;
}
.nm-vin .eq-bar:nth-child(6) {
height: 75%;
animation-duration: 0.5s;
animation-delay: 0.25s;
}
.nm-vin .eq-bar:nth-child(7) {
height: 50%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(8) {
height: 85%;
animation-duration: 0.4s;
animation-delay: 0.3s;
}
.nm-vin .eq-bar:nth-child(9) {
height: 60%;
animation-duration: 0.6s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(10) {
height: 35%;
animation-duration: 0.5s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(11) {
height: 80%;
animation-duration: 0.9s;
animation-delay: 0.05s;
}
.nm-vin .eq-bar:nth-child(12) {
height: 55%;
animation-duration: 0.6s;
animation-delay: 0.15s;
}
.nm-vin .eq-bar:nth-child(13) {
height: 45%;
animation-duration: 0.4s;
animation-delay: 0.25s;
}
.nm-vin .eq-bar:nth-child(14) {
height: 70%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(15) {
height: 90%;
animation-duration: 0.5s;
animation-delay: 0.35s;
}
.nm-vin .eq-bar:nth-child(16) {
height: 65%;
animation-duration: 0.8s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(17) {
height: 40%;
animation-duration: 0.6s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(18) {
height: 55%;
animation-duration: 0.4s;
animation-delay: 0.1s;
}
@keyframes nm-vin-eq {
0% {
transform: scaleY(0.3);
}
100% {
transform: scaleY(1);
}
}
.nm-vin .eq-wrap.paused .eq-bar {
animation-play-state: paused;
}
/* Controls */
.nm-vin .controls {
display: flex;
align-items: center;
justify-content: center;
gap: 18px;
}
.nm-vin .ctrl-btn {
border: none;
background: var(--nm-vin-bg);
border-radius: 50%;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--nm-vin-text-muted);
transition: all 0.15s;
}
.nm-vin .ctrl-btn.sm {
width: 40px;
height: 40px;
box-shadow: 4px 4px 10px var(--nm-vin-neu-dark), -3px -3px 8px var(--nm-vin-neu-light);
font-size: 12px;
}
.nm-vin .ctrl-btn.sm:active {
box-shadow: inset 2px 2px 6px var(--nm-vin-neu-dark), inset -1px -1px 4px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
}
.nm-vin .ctrl-btn.lg {
width: 62px;
height: 62px;
box-shadow: 7px 7px 16px var(--nm-vin-neu-dark), -6px -6px 14px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
font-size: 20px;
}
.nm-vin .ctrl-btn.lg:active {
box-shadow: inset 3px 3px 8px var(--nm-vin-neu-dark), inset -2px -2px 6px var(--nm-vin-neu-light);
}
/* Volume */
.nm-vin .volume-row {
display: flex;
align-items: center;
gap: 12px;
}
.nm-vin .vol-icon {
color: var(--nm-vin-text-muted);
font-size: 11px;
}
.nm-vin .vol-track {
flex: 1;
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow: inset 2px 2px 4px var(--nm-vin-neu-dark), inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
}
.nm-vin .vol-fill {
height: 100%;
width: 62%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
}
@media (prefers-reduced-motion: reduce) {
.nm-vin .disc,
.nm-vin .eq-bar {
animation: none;
}
}(() => {
const root = document.querySelector('.nm-vin');
if (!root) return;
let playing = false;
const disc = root.querySelector('#nm-vin-disc');
const eqWrap = root.querySelector('#nm-vin-eq');
const playBtn = root.querySelector('#nm-vin-play');
const pFill = root.querySelector('#nm-vin-fill');
const elapsedEl = root.querySelector('#nm-vin-elapsed');
let elapsed = 88;
const total = 232;
let interval = null;
const fmt = (s) => Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0');
const playIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><polygon points="6,4 20,12 6,20"/></svg>';
const pauseIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>';
if (playBtn) playBtn.addEventListener('click', () => {
playing = !playing;
if (disc) disc.classList.toggle('playing', playing);
if (playing) {
eqWrap?.classList.remove('paused');
playBtn.innerHTML = pauseIcon;
interval = setInterval(() => {
elapsed = Math.min(elapsed + 1, total);
if (pFill) pFill.style.width = (elapsed / total * 100) + '%';
if (elapsedEl) elapsedEl.textContent = fmt(elapsed);
if (elapsed >= total) { clearInterval(interval); playing = false; }
}, 1000);
} else {
eqWrap?.classList.add('paused');
clearInterval(interval);
playBtn.innerHTML = playIcon;
}
});
})();(() => {
const root = document.querySelector('.nm-vin');
if (!root) return;
let playing = false;
const disc = root.querySelector('#nm-vin-disc');
const eqWrap = root.querySelector('#nm-vin-eq');
const playBtn = root.querySelector('#nm-vin-play');
const pFill = root.querySelector('#nm-vin-fill');
const elapsedEl = root.querySelector('#nm-vin-elapsed');
let elapsed = 88;
const total = 232;
let interval = null;
const fmt = (s) => Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0');
const playIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><polygon points="6,4 20,12 6,20"/></svg>';
const pauseIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>';
if (playBtn) playBtn.addEventListener('click', () => {
playing = !playing;
if (disc) disc.classList.toggle('playing', playing);
if (playing) {
eqWrap?.classList.remove('paused');
playBtn.innerHTML = pauseIcon;
interval = setInterval(() => {
elapsed = Math.min(elapsed + 1, total);
if (pFill) pFill.style.width = (elapsed / total * 100) + '%';
if (elapsedEl) elapsedEl.textContent = fmt(elapsed);
if (elapsed >= total) { clearInterval(interval); playing = false; }
}, 1000);
} else {
eqWrap?.classList.add('paused');
clearInterval(interval);
playBtn.innerHTML = playIcon;
}
});
})();Here's a working CSS Neumorphism & Soft UI Design from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Vinyl Player
Source: https://codefronts.com/design-styles/css-neumorphism/vinyl-player/
A deep-slate dark-neumorphic music player with a spinning vinyl disc featuring real grooves via repeating-radial-gradient. Includes an 18-bar animated EQ visualizer synced to playback state, arc progress scrubber with glowing gradient thumb, tonearm SVG, amber accent label, and a full transport control set (shuffle, prev, play/pause, next, loop). DM Serif Display for warm sophistication. Best for music apps, podcast platforms, audio dashboards.
## HTML
```html
<section class="nm-vin" aria-label="Vinyl player widget">
<div class="card">
<span class="glow" aria-hidden="true"></span>
<span class="glow2" aria-hidden="true"></span>
<div class="disc-wrap">
<div class="disc-outer">
<div class="disc" id="nm-vin-disc">
<div class="disc-label" aria-hidden="true">♪</div>
</div>
<div class="tonearm-wrap" aria-hidden="true">
<svg width="70" height="80" viewBox="0 0 70 80">
<line x1="10" y1="10" x2="50" y2="65" stroke="#888" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="10" cy="10" r="6" fill="#444" stroke="#666" stroke-width="1"/>
<circle cx="50" cy="65" r="4" fill="#e0a86c" opacity="0.9"/>
</svg>
</div>
</div>
</div>
<div class="track-info">
<div class="track-title">Midnight Reverie</div>
<div class="track-artist">Aurelia Voss · Late Hours</div>
</div>
<div class="progress-wrap">
<div class="progress-track">
<div class="progress-fill" id="nm-vin-fill">
<span class="progress-thumb" aria-hidden="true"></span>
</div>
</div>
<div class="progress-times">
<span id="nm-vin-elapsed">1:28</span>
<span>3:52</span>
</div>
</div>
<div class="eq-wrap paused" id="nm-vin-eq" aria-hidden="true">
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
</div>
<div class="controls">
<button type="button" class="ctrl-btn sm" aria-label="Shuffle">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/>
<polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Previous">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="19,20 9,12 19,4"/><rect x="5" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn lg" id="nm-vin-play" aria-label="Play / Pause">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="6,4 20,12 6,20"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Next">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="5,4 15,12 5,20"/><rect x="17" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Loop">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/>
<polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/>
</svg>
</button>
</div>
<div class="volume-row">
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
</svg>
</span>
<div class="vol-track"><div class="vol-fill"></div></div>
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
</span>
</div>
</div>
</section>
```
## CSS
```css
/* ─── 02 Vinyl Player — dark-slate dark-neumorphic music widget ───── */
@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@300;400&display=swap');
.nm-vin {
--nm-vin-bg: #1e2230;
--nm-vin-neu-dark: #141720;
--nm-vin-neu-light: #282e40;
--nm-vin-text-primary: #d0d4e0;
--nm-vin-text-muted: #606880;
--nm-vin-accent: #e0a86c;
--nm-vin-accent2: #7c6cdb;
--nm-vin-inset: #1b1f2d;
position: relative;
width: 100%;
min-height: 720px;
background: var(--nm-vin-bg);
font-family: 'DM Mono', ui-monospace, monospace;
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
overflow: hidden;
box-sizing: border-box;
}
.nm-vin *,
.nm-vin *::before,
.nm-vin *::after {
box-sizing: border-box;
}
/* The inner card holds the dark neumorphic shadow */
.nm-vin .card {
position: relative;
width: 100%;
max-width: 400px;
background: var(--nm-vin-bg);
border-radius: 36px;
padding: 40px 34px 36px;
box-shadow: 14px 14px 32px var(--nm-vin-neu-dark), -10px -10px 28px var(--nm-vin-neu-light);
overflow: hidden;
}
/* Glows — contained inside the card */
.nm-vin .glow {
position: absolute;
width: 200px;
height: 200px;
border-radius: 50%;
background: radial-gradient(circle, rgba(124, 108, 219, 0.08), transparent 70%);
top: -60px;
right: -40px;
pointer-events: none;
}
.nm-vin .glow2 {
position: absolute;
width: 160px;
height: 160px;
border-radius: 50%;
background: radial-gradient(circle, rgba(224, 168, 108, 0.07), transparent 70%);
bottom: -40px;
left: -30px;
pointer-events: none;
}
/* Vinyl disc */
.nm-vin .disc-wrap {
display: flex;
justify-content: center;
position: relative;
}
.nm-vin .disc-outer {
width: 200px;
height: 200px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow: 10px 10px 24px var(--nm-vin-neu-dark), -8px -8px 20px var(--nm-vin-neu-light);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.nm-vin .disc {
width: 170px;
height: 170px;
border-radius: 50%;
background: repeating-radial-gradient(circle at 50% 50%,
transparent 0px, transparent 3px,
rgba(255, 255, 255, 0.02) 3px, rgba(255, 255, 255, 0.02) 4px
), conic-gradient(from 0deg,
#1a1d2a 0deg, #242838 90deg, #1a1d2a 180deg, #242838 270deg, #1a1d2a 360deg
);
box-shadow: inset 4px 4px 12px rgba(0, 0, 0, 0.6), inset -3px -3px 8px rgba(255, 255, 255, 0.04);
display: flex;
align-items: center;
justify-content: center;
animation: nm-vin-spin 4s linear infinite;
animation-play-state: paused;
}
.nm-vin .disc.playing {
animation-play-state: running;
}
@keyframes nm-vin-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.nm-vin .disc-label {
width: 62px;
height: 62px;
border-radius: 50%;
background: linear-gradient(135deg, var(--nm-vin-accent), #c4863a);
box-shadow: inset 2px 2px 6px rgba(0, 0, 0, 0.3), inset -1px -1px 3px rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: #1a1d2a;
}
/* Tonearm */
.nm-vin .tonearm-wrap {
position: absolute;
top: -8px;
right: -8px;
width: 70px;
height: 80px;
}
.nm-vin .tonearm-wrap svg {
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.6));
}
/* Track info */
.nm-vin .track-info {
text-align: center;
margin-bottom: 22px;
}
.nm-vin .track-title {
font-family: 'DM Serif Display', Georgia, serif;
font-size: 22px;
color: var(--nm-vin-text-primary);
margin-bottom: 5px;
letter-spacing: -0.3px;
}
.nm-vin .track-artist {
font-size: 10px;
font-weight: 300;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--nm-vin-text-muted);
}
/* Progress bar */
.nm-vin .progress-wrap {
margin-bottom: 22px;
position: relative;
}
.nm-vin .progress-track {
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow: inset 2px 2px 4px var(--nm-vin-neu-dark), inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
cursor: pointer;
}
.nm-vin .progress-fill {
height: 100%;
width: 38%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
position: relative;
}
.nm-vin .progress-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow: 2px 2px 6px var(--nm-vin-neu-dark), -1px -1px 4px var(--nm-vin-neu-light);
position: absolute;
right: -6px;
top: -4px;
}
.nm-vin .progress-times {
display: flex;
justify-content: space-between;
margin-top: 8px;
}
.nm-vin .progress-times span {
font-size: 9px;
color: var(--nm-vin-text-muted);
letter-spacing: 1px;
}
/* EQ bars */
.nm-vin .eq-wrap {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4px;
height: 30px;
margin-bottom: 24px;
}
.nm-vin .eq-bar {
display: block;
width: 4px;
border-radius: 2px;
background: linear-gradient(to top, var(--nm-vin-accent2), var(--nm-vin-accent));
animation: nm-vin-eq 0.6s ease-in-out infinite alternate;
opacity: 0.7;
}
.nm-vin .eq-bar:nth-child(1) {
height: 40%;
animation-duration: 0.5s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(2) {
height: 70%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(3) {
height: 90%;
animation-duration: 0.4s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(4) {
height: 60%;
animation-duration: 0.6s;
animation-delay: 0.05s;
}
.nm-vin .eq-bar:nth-child(5) {
height: 100%;
animation-duration: 0.8s;
animation-delay: 0.15s;
}
.nm-vin .eq-bar:nth-child(6) {
height: 75%;
animation-duration: 0.5s;
animation-delay: 0.25s;
}
.nm-vin .eq-bar:nth-child(7) {
height: 50%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(8) {
height: 85%;
animation-duration: 0.4s;
animation-delay: 0.3s;
}
.nm-vin .eq-bar:nth-child(9) {
height: 60%;
animation-duration: 0.6s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(10) {
height: 35%;
animation-duration: 0.5s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(11) {
height: 80%;
animation-duration: 0.9s;
animation-delay: 0.05s;
}
.nm-vin .eq-bar:nth-child(12) {
height: 55%;
animation-duration: 0.6s;
animation-delay: 0.15s;
}
.nm-vin .eq-bar:nth-child(13) {
height: 45%;
animation-duration: 0.4s;
animation-delay: 0.25s;
}
.nm-vin .eq-bar:nth-child(14) {
height: 70%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(15) {
height: 90%;
animation-duration: 0.5s;
animation-delay: 0.35s;
}
.nm-vin .eq-bar:nth-child(16) {
height: 65%;
animation-duration: 0.8s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(17) {
height: 40%;
animation-duration: 0.6s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(18) {
height: 55%;
animation-duration: 0.4s;
animation-delay: 0.1s;
}
@keyframes nm-vin-eq {
0% {
transform: scaleY(0.3);
}
100% {
transform: scaleY(1);
}
}
.nm-vin .eq-wrap.paused .eq-bar {
animation-play-state: paused;
}
/* Controls */
.nm-vin .controls {
display: flex;
align-items: center;
justify-content: center;
gap: 18px;
}
.nm-vin .ctrl-btn {
border: none;
background: var(--nm-vin-bg);
border-radius: 50%;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--nm-vin-text-muted);
transition: all 0.15s;
}
.nm-vin .ctrl-btn.sm {
width: 40px;
height: 40px;
box-shadow: 4px 4px 10px var(--nm-vin-neu-dark), -3px -3px 8px var(--nm-vin-neu-light);
font-size: 12px;
}
.nm-vin .ctrl-btn.sm:active {
box-shadow: inset 2px 2px 6px var(--nm-vin-neu-dark), inset -1px -1px 4px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
}
.nm-vin .ctrl-btn.lg {
width: 62px;
height: 62px;
box-shadow: 7px 7px 16px var(--nm-vin-neu-dark), -6px -6px 14px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
font-size: 20px;
}
.nm-vin .ctrl-btn.lg:active {
box-shadow: inset 3px 3px 8px var(--nm-vin-neu-dark), inset -2px -2px 6px var(--nm-vin-neu-light);
}
/* Volume */
.nm-vin .volume-row {
display: flex;
align-items: center;
gap: 12px;
}
.nm-vin .vol-icon {
color: var(--nm-vin-text-muted);
font-size: 11px;
}
.nm-vin .vol-track {
flex: 1;
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow: inset 2px 2px 4px var(--nm-vin-neu-dark), inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
}
.nm-vin .vol-fill {
height: 100%;
width: 62%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
}
@media (prefers-reduced-motion: reduce) {
.nm-vin .disc,
.nm-vin .eq-bar {
animation: none;
}
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('.nm-vin');
if (!root) return;
let playing = false;
const disc = root.querySelector('#nm-vin-disc');
const eqWrap = root.querySelector('#nm-vin-eq');
const playBtn = root.querySelector('#nm-vin-play');
const pFill = root.querySelector('#nm-vin-fill');
const elapsedEl = root.querySelector('#nm-vin-elapsed');
let elapsed = 88;
const total = 232;
let interval = null;
const fmt = (s) => Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0');
const playIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><polygon points="6,4 20,12 6,20"/></svg>';
const pauseIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>';
if (playBtn) playBtn.addEventListener('click', () => {
playing = !playing;
if (disc) disc.classList.toggle('playing', playing);
if (playing) {
eqWrap?.classList.remove('paused');
playBtn.innerHTML = pauseIcon;
interval = setInterval(() => {
elapsed = Math.min(elapsed + 1, total);
if (pFill) pFill.style.width = (elapsed / total * 100) + '%';
if (elapsedEl) elapsedEl.textContent = fmt(elapsed);
if (elapsed >= total) { clearInterval(interval); playing = false; }
}, 1000);
} else {
eqWrap?.classList.add('paused');
clearInterval(interval);
playBtn.innerHTML = playIcon;
}
});
})();
```Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Neumorphism & Soft UI Design from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Vinyl Player
Source: https://codefronts.com/design-styles/css-neumorphism/vinyl-player/
A deep-slate dark-neumorphic music player with a spinning vinyl disc featuring real grooves via repeating-radial-gradient. Includes an 18-bar animated EQ visualizer synced to playback state, arc progress scrubber with glowing gradient thumb, tonearm SVG, amber accent label, and a full transport control set (shuffle, prev, play/pause, next, loop). DM Serif Display for warm sophistication. Best for music apps, podcast platforms, audio dashboards.
## HTML
```html
<section class="nm-vin" aria-label="Vinyl player widget">
<div class="card">
<span class="glow" aria-hidden="true"></span>
<span class="glow2" aria-hidden="true"></span>
<div class="disc-wrap">
<div class="disc-outer">
<div class="disc" id="nm-vin-disc">
<div class="disc-label" aria-hidden="true">♪</div>
</div>
<div class="tonearm-wrap" aria-hidden="true">
<svg width="70" height="80" viewBox="0 0 70 80">
<line x1="10" y1="10" x2="50" y2="65" stroke="#888" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="10" cy="10" r="6" fill="#444" stroke="#666" stroke-width="1"/>
<circle cx="50" cy="65" r="4" fill="#e0a86c" opacity="0.9"/>
</svg>
</div>
</div>
</div>
<div class="track-info">
<div class="track-title">Midnight Reverie</div>
<div class="track-artist">Aurelia Voss · Late Hours</div>
</div>
<div class="progress-wrap">
<div class="progress-track">
<div class="progress-fill" id="nm-vin-fill">
<span class="progress-thumb" aria-hidden="true"></span>
</div>
</div>
<div class="progress-times">
<span id="nm-vin-elapsed">1:28</span>
<span>3:52</span>
</div>
</div>
<div class="eq-wrap paused" id="nm-vin-eq" aria-hidden="true">
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
</div>
<div class="controls">
<button type="button" class="ctrl-btn sm" aria-label="Shuffle">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/>
<polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Previous">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="19,20 9,12 19,4"/><rect x="5" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn lg" id="nm-vin-play" aria-label="Play / Pause">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="6,4 20,12 6,20"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Next">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="5,4 15,12 5,20"/><rect x="17" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Loop">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/>
<polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/>
</svg>
</button>
</div>
<div class="volume-row">
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
</svg>
</span>
<div class="vol-track"><div class="vol-fill"></div></div>
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
</span>
</div>
</div>
</section>
```
## CSS
```css
/* ─── 02 Vinyl Player — dark-slate dark-neumorphic music widget ───── */
@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@300;400&display=swap');
.nm-vin {
--nm-vin-bg: #1e2230;
--nm-vin-neu-dark: #141720;
--nm-vin-neu-light: #282e40;
--nm-vin-text-primary: #d0d4e0;
--nm-vin-text-muted: #606880;
--nm-vin-accent: #e0a86c;
--nm-vin-accent2: #7c6cdb;
--nm-vin-inset: #1b1f2d;
position: relative;
width: 100%;
min-height: 720px;
background: var(--nm-vin-bg);
font-family: 'DM Mono', ui-monospace, monospace;
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
overflow: hidden;
box-sizing: border-box;
}
.nm-vin *,
.nm-vin *::before,
.nm-vin *::after {
box-sizing: border-box;
}
/* The inner card holds the dark neumorphic shadow */
.nm-vin .card {
position: relative;
width: 100%;
max-width: 400px;
background: var(--nm-vin-bg);
border-radius: 36px;
padding: 40px 34px 36px;
box-shadow: 14px 14px 32px var(--nm-vin-neu-dark), -10px -10px 28px var(--nm-vin-neu-light);
overflow: hidden;
}
/* Glows — contained inside the card */
.nm-vin .glow {
position: absolute;
width: 200px;
height: 200px;
border-radius: 50%;
background: radial-gradient(circle, rgba(124, 108, 219, 0.08), transparent 70%);
top: -60px;
right: -40px;
pointer-events: none;
}
.nm-vin .glow2 {
position: absolute;
width: 160px;
height: 160px;
border-radius: 50%;
background: radial-gradient(circle, rgba(224, 168, 108, 0.07), transparent 70%);
bottom: -40px;
left: -30px;
pointer-events: none;
}
/* Vinyl disc */
.nm-vin .disc-wrap {
display: flex;
justify-content: center;
position: relative;
}
.nm-vin .disc-outer {
width: 200px;
height: 200px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow: 10px 10px 24px var(--nm-vin-neu-dark), -8px -8px 20px var(--nm-vin-neu-light);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.nm-vin .disc {
width: 170px;
height: 170px;
border-radius: 50%;
background: repeating-radial-gradient(circle at 50% 50%,
transparent 0px, transparent 3px,
rgba(255, 255, 255, 0.02) 3px, rgba(255, 255, 255, 0.02) 4px
), conic-gradient(from 0deg,
#1a1d2a 0deg, #242838 90deg, #1a1d2a 180deg, #242838 270deg, #1a1d2a 360deg
);
box-shadow: inset 4px 4px 12px rgba(0, 0, 0, 0.6), inset -3px -3px 8px rgba(255, 255, 255, 0.04);
display: flex;
align-items: center;
justify-content: center;
animation: nm-vin-spin 4s linear infinite;
animation-play-state: paused;
}
.nm-vin .disc.playing {
animation-play-state: running;
}
@keyframes nm-vin-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.nm-vin .disc-label {
width: 62px;
height: 62px;
border-radius: 50%;
background: linear-gradient(135deg, var(--nm-vin-accent), #c4863a);
box-shadow: inset 2px 2px 6px rgba(0, 0, 0, 0.3), inset -1px -1px 3px rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: #1a1d2a;
}
/* Tonearm */
.nm-vin .tonearm-wrap {
position: absolute;
top: -8px;
right: -8px;
width: 70px;
height: 80px;
}
.nm-vin .tonearm-wrap svg {
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.6));
}
/* Track info */
.nm-vin .track-info {
text-align: center;
margin-bottom: 22px;
}
.nm-vin .track-title {
font-family: 'DM Serif Display', Georgia, serif;
font-size: 22px;
color: var(--nm-vin-text-primary);
margin-bottom: 5px;
letter-spacing: -0.3px;
}
.nm-vin .track-artist {
font-size: 10px;
font-weight: 300;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--nm-vin-text-muted);
}
/* Progress bar */
.nm-vin .progress-wrap {
margin-bottom: 22px;
position: relative;
}
.nm-vin .progress-track {
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow: inset 2px 2px 4px var(--nm-vin-neu-dark), inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
cursor: pointer;
}
.nm-vin .progress-fill {
height: 100%;
width: 38%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
position: relative;
}
.nm-vin .progress-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow: 2px 2px 6px var(--nm-vin-neu-dark), -1px -1px 4px var(--nm-vin-neu-light);
position: absolute;
right: -6px;
top: -4px;
}
.nm-vin .progress-times {
display: flex;
justify-content: space-between;
margin-top: 8px;
}
.nm-vin .progress-times span {
font-size: 9px;
color: var(--nm-vin-text-muted);
letter-spacing: 1px;
}
/* EQ bars */
.nm-vin .eq-wrap {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4px;
height: 30px;
margin-bottom: 24px;
}
.nm-vin .eq-bar {
display: block;
width: 4px;
border-radius: 2px;
background: linear-gradient(to top, var(--nm-vin-accent2), var(--nm-vin-accent));
animation: nm-vin-eq 0.6s ease-in-out infinite alternate;
opacity: 0.7;
}
.nm-vin .eq-bar:nth-child(1) {
height: 40%;
animation-duration: 0.5s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(2) {
height: 70%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(3) {
height: 90%;
animation-duration: 0.4s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(4) {
height: 60%;
animation-duration: 0.6s;
animation-delay: 0.05s;
}
.nm-vin .eq-bar:nth-child(5) {
height: 100%;
animation-duration: 0.8s;
animation-delay: 0.15s;
}
.nm-vin .eq-bar:nth-child(6) {
height: 75%;
animation-duration: 0.5s;
animation-delay: 0.25s;
}
.nm-vin .eq-bar:nth-child(7) {
height: 50%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(8) {
height: 85%;
animation-duration: 0.4s;
animation-delay: 0.3s;
}
.nm-vin .eq-bar:nth-child(9) {
height: 60%;
animation-duration: 0.6s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(10) {
height: 35%;
animation-duration: 0.5s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(11) {
height: 80%;
animation-duration: 0.9s;
animation-delay: 0.05s;
}
.nm-vin .eq-bar:nth-child(12) {
height: 55%;
animation-duration: 0.6s;
animation-delay: 0.15s;
}
.nm-vin .eq-bar:nth-child(13) {
height: 45%;
animation-duration: 0.4s;
animation-delay: 0.25s;
}
.nm-vin .eq-bar:nth-child(14) {
height: 70%;
animation-duration: 0.7s;
animation-delay: 0.1s;
}
.nm-vin .eq-bar:nth-child(15) {
height: 90%;
animation-duration: 0.5s;
animation-delay: 0.35s;
}
.nm-vin .eq-bar:nth-child(16) {
height: 65%;
animation-duration: 0.8s;
animation-delay: 0.0s;
}
.nm-vin .eq-bar:nth-child(17) {
height: 40%;
animation-duration: 0.6s;
animation-delay: 0.2s;
}
.nm-vin .eq-bar:nth-child(18) {
height: 55%;
animation-duration: 0.4s;
animation-delay: 0.1s;
}
@keyframes nm-vin-eq {
0% {
transform: scaleY(0.3);
}
100% {
transform: scaleY(1);
}
}
.nm-vin .eq-wrap.paused .eq-bar {
animation-play-state: paused;
}
/* Controls */
.nm-vin .controls {
display: flex;
align-items: center;
justify-content: center;
gap: 18px;
}
.nm-vin .ctrl-btn {
border: none;
background: var(--nm-vin-bg);
border-radius: 50%;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--nm-vin-text-muted);
transition: all 0.15s;
}
.nm-vin .ctrl-btn.sm {
width: 40px;
height: 40px;
box-shadow: 4px 4px 10px var(--nm-vin-neu-dark), -3px -3px 8px var(--nm-vin-neu-light);
font-size: 12px;
}
.nm-vin .ctrl-btn.sm:active {
box-shadow: inset 2px 2px 6px var(--nm-vin-neu-dark), inset -1px -1px 4px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
}
.nm-vin .ctrl-btn.lg {
width: 62px;
height: 62px;
box-shadow: 7px 7px 16px var(--nm-vin-neu-dark), -6px -6px 14px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
font-size: 20px;
}
.nm-vin .ctrl-btn.lg:active {
box-shadow: inset 3px 3px 8px var(--nm-vin-neu-dark), inset -2px -2px 6px var(--nm-vin-neu-light);
}
/* Volume */
.nm-vin .volume-row {
display: flex;
align-items: center;
gap: 12px;
}
.nm-vin .vol-icon {
color: var(--nm-vin-text-muted);
font-size: 11px;
}
.nm-vin .vol-track {
flex: 1;
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow: inset 2px 2px 4px var(--nm-vin-neu-dark), inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
}
.nm-vin .vol-fill {
height: 100%;
width: 62%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
}
@media (prefers-reduced-motion: reduce) {
.nm-vin .disc,
.nm-vin .eq-bar {
animation: none;
}
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('.nm-vin');
if (!root) return;
let playing = false;
const disc = root.querySelector('#nm-vin-disc');
const eqWrap = root.querySelector('#nm-vin-eq');
const playBtn = root.querySelector('#nm-vin-play');
const pFill = root.querySelector('#nm-vin-fill');
const elapsedEl = root.querySelector('#nm-vin-elapsed');
let elapsed = 88;
const total = 232;
let interval = null;
const fmt = (s) => Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0');
const playIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><polygon points="6,4 20,12 6,20"/></svg>';
const pauseIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>';
if (playBtn) playBtn.addEventListener('click', () => {
playing = !playing;
if (disc) disc.classList.toggle('playing', playing);
if (playing) {
eqWrap?.classList.remove('paused');
playBtn.innerHTML = pauseIcon;
interval = setInterval(() => {
elapsed = Math.min(elapsed + 1, total);
if (pFill) pFill.style.width = (elapsed / total * 100) + '%';
if (elapsedEl) elapsedEl.textContent = fmt(elapsed);
if (elapsed >= total) { clearInterval(interval); playing = false; }
}, 1000);
} else {
eqWrap?.classList.add('paused');
clearInterval(interval);
playBtn.innerHTML = playIcon;
}
});
})();
```