18 CSS Play / Pause Button Designs
Voice Memo
Record-style red button with a tiny live waveform indicator. The waveform animates while playing back; freezes when paused. Specifically tuned for voice notes, podcasts, and dictation UIs.
Voice Memo the 11th of 18 designs in the 18 CSS Play / Pause Button Designs collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
The code
<button class="pp-mem" type="button" aria-pressed="false" aria-label="Play voice memo" data-pp>
<span class="pp-mem-dot" aria-hidden="true"></span>
<span class="pp-mem-wave" aria-hidden="true">
<span></span><span></span><span></span><span></span><span></span> <span></span><span></span
><span></span><span></span>
</span>
<span class="pp-mem-time">0:14</span>
</button> .pp-mem {
display: inline-grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 10px;
padding: 8px 14px 8px 10px;
background: #15151d;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 999px;
color: #f0eeff;
cursor: pointer;
font-family: "JetBrains Mono", monospace;
transition:
border-color 0.2s,
background 0.2s;
}
.pp-mem:hover {
border-color: rgba(239, 68, 68, 0.4);
background: #1a1a25;
}
.pp-mem:focus-visible {
outline: 3px solid rgba(239, 68, 68, 0.4);
outline-offset: 3px;
}
.pp-mem-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #ef4444;
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.5);
transition: background 0.2s;
}
.pp-mem[aria-pressed="true"] .pp-mem-dot {
animation: ppMemPing 1.4s ease-out infinite;
}
@keyframes ppMemPing {
0% {
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.6);
}
100% {
box-shadow: 0 0 0 8px rgba(239, 68, 68, 0);
}
}
.pp-mem-wave {
display: flex;
align-items: center;
gap: 2px;
height: 18px;
}
.pp-mem-wave > span {
width: 2px;
height: 30%;
background: #94a3b8;
border-radius: 1px;
transform-origin: center;
}
.pp-mem-wave > span:nth-child(1) {
height: 50%;
}
.pp-mem-wave > span:nth-child(2) {
height: 80%;
}
.pp-mem-wave > span:nth-child(3) {
height: 65%;
}
.pp-mem-wave > span:nth-child(4) {
height: 95%;
}
.pp-mem-wave > span:nth-child(5) {
height: 70%;
}
.pp-mem-wave > span:nth-child(6) {
height: 90%;
}
.pp-mem-wave > span:nth-child(7) {
height: 55%;
}
.pp-mem-wave > span:nth-child(8) {
height: 75%;
}
.pp-mem-wave > span:nth-child(9) {
height: 40%;
}
.pp-mem[aria-pressed="true"] .pp-mem-wave > span {
background: #ef4444;
animation: ppMemWave 0.6s ease-in-out infinite alternate;
}
.pp-mem[aria-pressed="true"] .pp-mem-wave > span:nth-child(2n) {
animation-delay: 0.1s;
}
.pp-mem[aria-pressed="true"] .pp-mem-wave > span:nth-child(3n) {
animation-delay: 0.2s;
}
@keyframes ppMemWave {
from {
transform: scaleY(0.5);
}
to {
transform: scaleY(1.1);
}
}
.pp-mem-time {
font-size: 11px;
font-weight: 600;
color: #94a3b8;
font-variant-numeric: tabular-nums;
letter-spacing: 0.04em;
}
@media (prefers-reduced-motion: reduce) {
.pp-mem,
.pp-mem * {
animation: none !important;
}
}
// ── Drop this on every page where you render a play/pause button ──
// Toggles aria-pressed + aria-label on click. The CSS handles all visuals.
document.querySelectorAll('[data-pp]').forEach(function (btn) {
btn.addEventListener('click', function () {
var playing = btn.getAttribute('aria-pressed') === 'true';
btn.setAttribute('aria-pressed', String(!playing));
btn.setAttribute('aria-label', !playing ? 'Pause' : 'Play');
});
}); More from 18 CSS Play / Pause Button Designs
Equaliser BarsSkip HubLiquid WavePulse HaloToggle PillNeon ArcadeMinimal OutlineGradient DiscFloating FABProgress RingMagnetic HoverLiquid Drop
View the full collection →