V-008 Media medium

Retro Vinyl Record Player レトロレコードプレーヤー

Interactive vinyl turntable visual simulation featuring spinning vinyl, tonearm tracking, and playback toggle controls. レコード盤の回転とアームを操作する視覚シミュレータ。(音声なし)

ライブデモ Live Demo

Visual simulation only - no audio output

概要・用途・特徴Overview, Usage & Features

何ができるかWhat it does

Interactive vinyl turntable visual simulation featuring spinning vinyl, tonearm tracking, and playback toggle controls.

レコード盤の回転とアームを操作する視覚シミュレータ。(音声なし)

どこで使うかWhere to use

Portfolio, product showcase, landing page.

ポートフォリオ、製品紹介、ランディングページ。

特徴Key features

Interactive vinyl turntable visual simulation featuring spinning vinyl, tonearm tracking, and playback toggle controls.

レコード盤の回転とアームを操作する視覚シミュレータ。(音声なし)

調整可能パラメータ Adjustable Parameters

Parameter Default Description
--demo-accent#d4af37Accent color for the panel border and keyboard focus ring.

実装コード Implementation Code

<div class="demo-stage vp-root" role="region" aria-label="Vinyl Record Player Simulation"><div class="vp-deck"><div class="vp-platter"><div class="vp-vinyl" aria-hidden="true"><div class="vp-groove vp-groove-1"></div><div class="vp-groove vp-groove-2"></div><div class="vp-label"><span class="vp-label-title">SIDE A</span><span class="vp-label-rpm">33 RPM</span></div><div class="vp-spindle"></div></div><div class="vp-tonearm" aria-hidden="true"><div class="vp-tonearm-base"></div><div class="vp-tonearm-arm"></div><div class="vp-tonearm-head"></div></div></div><div class="vp-controls"><div class="vp-status-bar"><span class="vp-status-light" aria-hidden="true"></span><span class="vp-status-text">Visual simulation only - no audio output</span></div><div class="vp-btn-group"><button type="button" class="vp-play-btn" aria-pressed="false" aria-label="Toggle vinyl playback">Play</button></div></div></div></div>
<script>
document.querySelectorAll('.vp-root').forEach(root => {
  const playBtn = root.querySelector('.vp-play-btn');
  if (!playBtn) return;
  const motionOk = window.matchMedia('(prefers-reduced-motion: no-preference)').matches;
  if (!motionOk) {
    playBtn.disabled = true;
    playBtn.setAttribute('aria-label', 'Animation disabled due to reduced motion preference');
    playBtn.textContent = 'Reduced Motion';
    return;
  }
  playBtn.addEventListener('click', () => {
    const isPlaying = root.classList.toggle('is-playing');
    playBtn.setAttribute('aria-pressed', isPlaying ? 'true' : 'false');
    playBtn.textContent = isPlaying ? 'Pause' : 'Play';
  });
});
</script>
.vp-root {
  box-sizing: border-box;
  width: 100%;
  max-width: 560px;
  max-height: 420px;
  min-width: 0;
  margin: 0 auto;
  padding: 16px;
  background: radial-gradient(circle at 50% 15%, #2c2522 0%, #151210 100%);
  border-radius: 16px;
  border: 1px solid #443733;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.65);
  font-family: system-ui, -apple-system, sans-serif;
  color: #f5ebe1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.vp-root *, .vp-root *::before, .vp-root *::after {
  box-sizing: border-box;
  min-width: 0;
}
.vp-root .vp-deck {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
.vp-root .vp-platter {
  position: relative;
  width: 176px;
  height: 176px;
  background: #110e0d;
  border-radius: 50%;
  box-shadow: inset 0 0 18px rgba(0, 0, 0, 0.95), 0 8px 24px rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.vp-root .vp-vinyl {
  position: relative;
  width: 160px;
  height: 160px;
  border-radius: 50%;
  background: repeating-radial-gradient(circle at center, #1c1a19 0px, #0e0d0c 2px, #211f1e 4px);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.8);
}
.vp-root .vp-groove-1 {
  position: absolute;
  width: 126px;
  height: 126px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.05);
}
.vp-root .vp-groove-2 {
  position: absolute;
  width: 92px;
  height: 92px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.07);
}
.vp-root .vp-label {
  position: relative;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: radial-gradient(circle, #d9531e, #962800);
  border: 2px solid #241d1a;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
.vp-root .vp-label-title {
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.5px;
}
.vp-root .vp-label-rpm {
  font-size: 7px;
  opacity: 0.85;
}
.vp-root .vp-spindle {
  position: absolute;
  width: 8px;
  height: 8px;
  background: #d4af37;
  border-radius: 50%;
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
}
.vp-root .vp-tonearm {
  position: absolute;
  top: 8px;
  right: -8px;
  width: 48px;
  height: 116px;
  pointer-events: none;
  transform-origin: 36px 14px;
  transform: rotate(-24deg);
}
.vp-root.is-playing .vp-tonearm {
  transform: rotate(10deg);
}
.vp-root .vp-tonearm-base {
  position: absolute;
  top: 6px;
  right: 4px;
  width: 16px;
  height: 16px;
  background: #d4af37;
  border-radius: 50%;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
}
.vp-root .vp-tonearm-arm {
  position: absolute;
  top: 14px;
  right: 11px;
  width: 3px;
  height: 86px;
  background: linear-gradient(to right, #cfcfcf, #858585);
  border-radius: 2px;
}
.vp-root .vp-tonearm-head {
  position: absolute;
  bottom: 12px;
  left: 28px;
  width: 10px;
  height: 18px;
  background: #1c1c1c;
  border-radius: 2px;
  border-left: 2px solid #d4af37;
}
.vp-root .vp-controls {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.vp-root .vp-status-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: #b5a79c;
  text-align: center;
  flex-wrap: wrap;
  justify-content: center;
}
.vp-root .vp-status-light {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #554a44;
}
.vp-root.is-playing .vp-status-light {
  background: #22c55e;
  box-shadow: 0 0 8px #22c55e;
}
.vp-root .vp-play-btn {
  appearance: none;
  border: 1px solid #d4af37;
  background: linear-gradient(135deg, #3d2f28, #221a16);
  color: #f7e6c4;
  padding: 8px 24px;
  border-radius: 22px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.vp-root .vp-play-btn:hover {
  background: linear-gradient(135deg, #4d3c33, #2d221c);
}
.vp-root .vp-play-btn:focus-visible {
  outline: 2px solid #e6a73c;
  outline-offset: 2px;
}
.vp-root .vp-play-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
@media (prefers-reduced-motion: no-preference) {
  .vp-root .vp-vinyl {
    transition: transform 0.4s ease;
  }
  .vp-root.is-playing .vp-vinyl {
    animation: vp-spin 2.2s linear infinite;
  }
  .vp-root .vp-tonearm {
    transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
  }
  .vp-root .vp-play-btn {
    transition: background 0.2s ease, transform 0.1s ease;
  }
  .vp-root .vp-play-btn:active {
    transform: scale(0.97);
  }
  .vp-root .vp-status-light {
    transition: background 0.3s ease, box-shadow 0.3s ease;
  }
}
@keyframes vp-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.vp-root{max-height:none;--demo-accent:#d4af37;border-color:var(--demo-accent)}.vp-root button:focus-visible{outline-color:var(--demo-accent)}
import { useState, useEffect } from "react";
import "./V-008.css";
function useReducedMotion() {
  const [reduced, setReduced] = useState(true);
  useEffect(() => {
    const media = window.matchMedia("(prefers-reduced-motion: reduce)");
    const sync = () => setReduced(media.matches);
    sync();
    media.addEventListener("change", sync);
    return () => media.removeEventListener("change", sync);
  }, []);
  return reduced;
}
function VinylRecordPlayer() {
  const [playing, setPlaying] = useState(false);
  const reduced = useReducedMotion();
  return <div role="region" aria-label="Vinyl Record Player Simulation" className={"demo-stage vp-root" + (playing && !reduced ? " is-playing" : "")}><div className="vp-deck"><div className="vp-platter"><div className="vp-vinyl" aria-hidden="true"><div className="vp-groove vp-groove-1" /><div className="vp-groove vp-groove-2" /><div className="vp-label"><span className="vp-label-title">{"SIDE A"}</span><span className="vp-label-rpm">{"33 RPM"}</span></div><div className="vp-spindle" /></div><div className="vp-tonearm" aria-hidden="true"><div className="vp-tonearm-base" /><div className="vp-tonearm-arm" /><div className="vp-tonearm-head" /></div></div><div className="vp-controls"><div className="vp-status-bar"><span className="vp-status-light" aria-hidden="true" /><span className="vp-status-text">{"Visual simulation only - no audio output"}</span></div><div className="vp-btn-group"><button type="button" className="vp-play-btn" disabled={reduced} aria-pressed={playing && !reduced} onClick={() => setPlaying((value) => !value)} aria-label={reduced ? "Animation disabled by reduced motion preference" : "Toggle animation playback"}>{reduced ? "REDUCED MOTION" : playing ? "Pause" : "Play"}</button></div></div></div></div>;
}
export {
  VinylRecordPlayer as default
};
.vp-root {
  box-sizing: border-box;
  width: 100%;
  max-width: 560px;
  max-height: 420px;
  min-width: 0;
  margin: 0 auto;
  padding: 16px;
  background: radial-gradient(circle at 50% 15%, #2c2522 0%, #151210 100%);
  border-radius: 16px;
  border: 1px solid #443733;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.65);
  font-family: system-ui, -apple-system, sans-serif;
  color: #f5ebe1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.vp-root *, .vp-root *::before, .vp-root *::after {
  box-sizing: border-box;
  min-width: 0;
}
.vp-root .vp-deck {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
.vp-root .vp-platter {
  position: relative;
  width: 176px;
  height: 176px;
  background: #110e0d;
  border-radius: 50%;
  box-shadow: inset 0 0 18px rgba(0, 0, 0, 0.95), 0 8px 24px rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.vp-root .vp-vinyl {
  position: relative;
  width: 160px;
  height: 160px;
  border-radius: 50%;
  background: repeating-radial-gradient(circle at center, #1c1a19 0px, #0e0d0c 2px, #211f1e 4px);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.8);
}
.vp-root .vp-groove-1 {
  position: absolute;
  width: 126px;
  height: 126px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.05);
}
.vp-root .vp-groove-2 {
  position: absolute;
  width: 92px;
  height: 92px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.07);
}
.vp-root .vp-label {
  position: relative;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: radial-gradient(circle, #d9531e, #962800);
  border: 2px solid #241d1a;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
.vp-root .vp-label-title {
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.5px;
}
.vp-root .vp-label-rpm {
  font-size: 7px;
  opacity: 0.85;
}
.vp-root .vp-spindle {
  position: absolute;
  width: 8px;
  height: 8px;
  background: #d4af37;
  border-radius: 50%;
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
}
.vp-root .vp-tonearm {
  position: absolute;
  top: 8px;
  right: -8px;
  width: 48px;
  height: 116px;
  pointer-events: none;
  transform-origin: 36px 14px;
  transform: rotate(-24deg);
}
.vp-root.is-playing .vp-tonearm {
  transform: rotate(10deg);
}
.vp-root .vp-tonearm-base {
  position: absolute;
  top: 6px;
  right: 4px;
  width: 16px;
  height: 16px;
  background: #d4af37;
  border-radius: 50%;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
}
.vp-root .vp-tonearm-arm {
  position: absolute;
  top: 14px;
  right: 11px;
  width: 3px;
  height: 86px;
  background: linear-gradient(to right, #cfcfcf, #858585);
  border-radius: 2px;
}
.vp-root .vp-tonearm-head {
  position: absolute;
  bottom: 12px;
  left: 28px;
  width: 10px;
  height: 18px;
  background: #1c1c1c;
  border-radius: 2px;
  border-left: 2px solid #d4af37;
}
.vp-root .vp-controls {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.vp-root .vp-status-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: #b5a79c;
  text-align: center;
  flex-wrap: wrap;
  justify-content: center;
}
.vp-root .vp-status-light {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #554a44;
}
.vp-root.is-playing .vp-status-light {
  background: #22c55e;
  box-shadow: 0 0 8px #22c55e;
}
.vp-root .vp-play-btn {
  appearance: none;
  border: 1px solid #d4af37;
  background: linear-gradient(135deg, #3d2f28, #221a16);
  color: #f7e6c4;
  padding: 8px 24px;
  border-radius: 22px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.vp-root .vp-play-btn:hover {
  background: linear-gradient(135deg, #4d3c33, #2d221c);
}
.vp-root .vp-play-btn:focus-visible {
  outline: 2px solid #e6a73c;
  outline-offset: 2px;
}
.vp-root .vp-play-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
@media (prefers-reduced-motion: no-preference) {
  .vp-root .vp-vinyl {
    transition: transform 0.4s ease;
  }
  .vp-root.is-playing .vp-vinyl {
    animation: vp-spin 2.2s linear infinite;
  }
  .vp-root .vp-tonearm {
    transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
  }
  .vp-root .vp-play-btn {
    transition: background 0.2s ease, transform 0.1s ease;
  }
  .vp-root .vp-play-btn:active {
    transform: scale(0.97);
  }
  .vp-root .vp-status-light {
    transition: background 0.3s ease, box-shadow 0.3s ease;
  }
}
@keyframes vp-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.vp-root{max-height:none;--demo-accent:#d4af37;border-color:var(--demo-accent)}.vp-root button:focus-visible{outline-color:var(--demo-accent)}

仕組みとカスタマイズHow It Works & Customization

仕組みHow it works

Scoped CSS creates the visual design; small vanilla JavaScript handlers update the local demo state.

スコープ化したCSSで描画し、Vanilla JavaScriptでデモ内の状態だけを更新します。

カスタマイズ方法Customization

Accent color for the panel border and keyboard focus ring.

パネルの枠線とキーボードフォーカスの色を変更します。

注意点Caveats

The sample content is fictional. No external assets or libraries are required. Motion follows the operating system preference. Demo labels are English; surrounding documentation supports Japanese and English.

デモ内容は架空です。外部素材・ライブラリは不要。OSの動きを減らす設定に対応します。デモ内のラベルは英語、解説は日英対応です。

よくある質問 Frequently Asked Questions

Does this demo need external libraries? 外部ライブラリは必要ですか?

The sample content is fictional. No external assets or libraries are required. Motion follows the operating system preference. Demo labels are English; surrounding documentation supports Japanese and English.

デモ内容は架空です。外部素材・ライブラリは不要。OSの動きを減らす設定に対応します。デモ内のラベルは英語、解説は日英対応です。

AIへの指示テンプレート AI Prompt Template

以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.