B-Side Audio and Vision

Sound.
Code.
Vision.
Musikproduktion, Fullstack-Entwicklung und Videoproduktion aus Nürnberg

Über mich

Hey, ich bin Ben.

20 Jahre alt. Musikproduzent, Mixing & Mastering Engineer, IT-Developer — und Gründer von B-Side Audio and Vision.

Seit knapp 4 Jahren produziere ich Musik und verfeinere Sounds bis ins kleinste Detail. Parallel studiere ich Informatik und baue Websites, Skripte und individuelle Software-Lösungen.

B-Side steht für die andere Seite — die, die oft mehr Charakter hat als die A-Seite.

Musikproduktion

Meine Beats

Eigene Produktionen, gemischt und gemastert im eigenen Studio. Track auswählen — Nadel drauf.

Jetzt läuft
Track auswählen
B-Side Audio and Vision
— BPM
0:00
0:00
IT-Lösungen

Digitale Lösungen

src/components/VinylPlayer.astroCode aus dieser Seite
---export interface Track {  title: string;  meta: string;  bpm: number;  duration: number;  color: string;  src?: string; }export interface Props {  tracks: Track[];}const { tracks } = Astro.props;---Zeile 17–540: Markup & CSS<script define:vars={{ tracks }}>  const ARM_PARKED = -30;  const ARM_START  = -25;  const ARM_END    = 10;  let currentTrack = null;  let isPlaying    = false;  let progress     = 0;  let ticker       = null;  let bpmTicker    = null;  let waveHeights  = [];  let audio        = null;  const vinyl        = document.getElementById('vinyl');  const tonearm      = document.getElementById('tonearm');  const platterRing  = document.getElementById('platterRing');  const playBtn      = document.getElementById('playBtn');  const playIcon     = document.getElementById('playIcon');  const volIconBtn   = document.getElementById('volIconBtn');  const volIcon      = document.getElementById('volIcon');  const trackTitle   = document.getElementById('trackTitle');  const trackMeta    = document.getElementById('trackMeta');  const bpmLabel     = document.getElementById('bpmLabel');  const bpmDot       = document.getElementById('bpmDot');  const nowPlaying     = document.getElementById('nowPlayingLabel');  const nowPlayingText = document.getElementById('nowPlayingText');  const progressBar    = document.getElementById('progressBar');  const progressFill   = document.getElementById('progressFill');  const timeElapsed    = document.getElementById('timeElapsed');  const timeTotal      = document.getElementById('timeTotal');  const waveformEl     = document.getElementById('waveform');  let lastVolume = 0.8;  function fmt(s) {    if (!Number.isFinite(s) || s < 0) s = 0;    return Math.floor(s/60) + ':' + String(Math.floor(s%60)).padStart(2,'0');  }  function setPlaybackState(state) {    nowPlaying.classList.toggle('visible', state !== 'stopped');    nowPlaying.dataset.state = state;    if (state !== 'stopped') nowPlayingText.textContent = state === 'playing' ? 'Jetzt läuft' : 'Pausiert';  }  function setProgress(pct, elapsedSeconds) {    progress = Math.max(0, Math.min(100, pct));    const secs = elapsedSeconds !== undefined      ? elapsedSeconds      : (progress / 100) * (currentTrack === null ? 0 : tracks[currentTrack].duration);    progressFill.style.transform = 'scaleX(' + progress / 100 + ')';    timeElapsed.textContent = fmt(secs);    progressBar.setAttribute('aria-valuenow', String(Math.round(progress)));    progressBar.setAttribute('aria-valuetext', fmt(secs));    drawWave(progress);    setArm(pctToAngle(progress));  }  function seekFromPointer(el, clientX) {    if (currentTrack === null) return;    const rect = el.getBoundingClientRect();    const pct = ((clientX - rect.left) / rect.width) * 100;    const clamped = Math.max(0, Math.min(100, pct));    if (audio && Number.isFinite(audio.duration)) audio.currentTime = (clamped / 100) * audio.duration;    setProgress(clamped);  }  function pctToAngle(pct) {    return ARM_START + (ARM_END - ARM_START) * (pct / 100);  }  function setArm(deg) {    tonearm.style.transform = 'rotate(' + deg + 'deg)';  }  function genWave() {    waveHeights = Array.from({length: 48}, () => 8 + Math.random() * 36);    waveformEl.innerHTML = '';    waveHeights.forEach(h => {      const b = document.createElement('div');      b.style.cssText = `flex:1;height:${h}px;min-height:4px;border-radius:2px;background:var(--wave-unplayed,rgba(255,255,255,0.18));transition:background 0.15s;`;      waveformEl.appendChild(b);    });  }  function drawWave(pct) {    const bars = waveformEl.children;    const played = Math.floor((pct / 100) * waveHeights.length);    for (let i = 0; i < bars.length; i++) {      bars[i].style.background = i < played ? 'var(--accent)'        : i === played && isPlaying ? 'var(--accent)'        : 'var(--wave-unplayed, rgba(255,255,255,0.18))';    }  }  //reduced motion keeps only the fade  const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');  function revealTrackInfo() {    const frames = reduceMotion.matches      ? [{ opacity: 0 }, { opacity: 1 }]      : [{ opacity: 0, transform: 'translateY(6px)' }, { opacity: 1, transform: 'none' }];    [trackTitle, trackMeta].forEach((el, i) => el.animate(frames, {      duration: 240,      delay: i * 40,      easing: 'cubic-bezier(0.23, 1, 0.32, 1)',      fill: 'backwards',    }));  }  function hexRgba(hex, a) {    const r = parseInt(hex.slice(1,3),16);    const g = parseInt(hex.slice(3,5),16);    const b = parseInt(hex.slice(5,7),16);    return 'rgba('+r+','+g+','+b+','+a+')';  }  function selectTrack(i) {    if (currentTrack === i && isPlaying) { pause(); return; }    currentTrack = i;    progress = 0;    const t = tracks[i];    document.querySelectorAll('.track-btn').forEach((b,j) =>      b.classList.toggle('active', j === i));    trackTitle.textContent = t.title;    trackMeta.textContent  = t.meta;    revealTrackInfo();    bpmLabel.textContent   = t.bpm + ' BPM';    timeTotal.textContent  = fmt(t.duration);    document.documentElement.style.setProperty('--accent', t.color);    document.documentElement.style.setProperty('--accent-dim', hexRgba(t.color, 0.1));    //audio support    if (audio) { audio.pause(); audio = null; }    if (t.src) {      audio = new Audio(t.src);      audio.volume = document.getElementById('volSlider').value / 100;    }    genWave();    drawWave(0);    play();  }  function clearBeatGlow() {    vinyl.style.removeProperty('--beat-glow');    vinyl.style.removeProperty('--beat-glow-soft');  }  function play() {    isPlaying = true;    vinyl.classList.add('playing');    vinyl.style.animationPlayState = 'running';    platterRing.classList.add('active');    playBtn.setAttribute('aria-label', 'Pausieren');    playIcon.innerHTML = '<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>';    setPlaybackState('playing');    setArm(pctToAngle(progress));    if (audio) audio.play().catch(() => {});    // bpm pulse    clearInterval(bpmTicker);    bpmTicker = setInterval(() => {      bpmDot.style.transform = 'scale(1.5)';      const c = tracks[currentTrack].color;      vinyl.style.setProperty('--beat-glow', hexRgba(c, 0.20));      vinyl.style.setProperty('--beat-glow-soft', hexRgba(c, 0.07));      const beatMs = (60 / tracks[currentTrack].bpm) * 1000 * 0.5;      setTimeout(() => {        bpmDot.style.transform = '';        clearBeatGlow();      }, beatMs);    }, (60 / tracks[currentTrack].bpm) * 1000);    // progress tick    clearInterval(ticker);    const t = tracks[currentTrack];    if (audio) {      // sync with audio      ticker = setInterval(() => {        if (!audio) return;        setProgress((audio.currentTime / audio.duration) * 100 || 0, audio.currentTime);        if (audio.ended) {          clearInterval(ticker);          clearInterval(bpmTicker);          nextTrack();        }      }, 100);    } else {      // simulated progress      const stepPerTick = (100 / t.duration) * 0.1;      ticker = setInterval(() => {        setProgress(progress + stepPerTick);        if (progress >= 100) {          clearInterval(ticker);          clearInterval(bpmTicker);          nextTrack();        }      }, 100);    }  }  function pause() {    isPlaying = false;    vinyl.style.animationPlayState = 'paused';    platterRing.classList.remove('active');    clearInterval(ticker);    clearInterval(bpmTicker);    bpmDot.style.transform = '';    clearBeatGlow();    playBtn.setAttribute('aria-label', 'Abspielen');    playIcon.innerHTML = '<path d="M8 5v14l11-7z"/>';    setPlaybackState('paused');    if (audio) audio.pause();  }  function nextTrack() {    if (currentTrack === null) { selectTrack(0); return; }    setArm(ARM_PARKED);    const next = currentTrack + 1;    if (next >= tracks.length) {      setTimeout(() => {        isPlaying = false;        vinyl.classList.remove('playing');        vinyl.style.animationPlayState = '';        platterRing.classList.remove('active');        bpmDot.style.transform = '';        clearBeatGlow();        playBtn.setAttribute('aria-label', 'Abspielen');        playIcon.innerHTML = '<path d="M8 5v14l11-7z"/>';        setPlaybackState('stopped');        setProgress(0, 0);      }, 600);    } else {      setTimeout(() => selectTrack(next), 600);    }  }  function prevTrack() {    if (currentTrack === null) { selectTrack(0); return; }    setArm(ARM_PARKED);    setTimeout(() => selectTrack((currentTrack - 1 + tracks.length) % tracks.length), 600);  }  // events  playBtn.onclick = () => {    if (currentTrack === null) { selectTrack(0); return; }    if (isPlaying) pause(); else play();  };  document.getElementById('nextBtn').onclick = nextTrack;  document.getElementById('prevBtn').onclick = prevTrack;  progressBar.onclick = (e) => seekFromPointer(progressBar, e.clientX);  waveformEl.onclick  = (e) => seekFromPointer(waveformEl, e.clientX);  progressBar.addEventListener('keydown', (e) => {    if (currentTrack === null) return;    const step = e.shiftKey ? 10 : 5;    let next = progress;    switch (e.key) {      case 'ArrowRight': case 'ArrowUp':   next = progress + step; break;      case 'ArrowLeft':  case 'ArrowDown': next = progress - step; break;      case 'PageUp':   next = progress + 10; break;      case 'PageDown': next = progress - 10; break;      case 'Home': next = 0;   break;      case 'End':  next = 100; break;      default: return;    }    e.preventDefault();    const clamped = Math.max(0, Math.min(100, next));    if (audio && Number.isFinite(audio.duration)) audio.currentTime = (clamped / 100) * audio.duration;    setProgress(clamped);  });  document.getElementById('volSlider').oninput = (e) => {    const v = Number(e.target.value);    if (audio) audio.volume = v / 100;    volIcon.textContent = v === 0 ? '🔇' : '🔈';    if (v > 0) lastVolume = v / 100;  };  volIconBtn.addEventListener('click', () => {    const slider = document.getElementById('volSlider');    const currentVolume = Number(slider.value);    if (currentVolume !== 0) {      lastVolume = currentVolume / 100;      slider.value = '0';      if (audio) audio.volume = 0;      volIcon.textContent = '🔇';    } else {      slider.value = String(Math.round(lastVolume * 100));      if (audio) audio.volume = lastVolume;      volIcon.textContent = '🔈';    }  });  document.querySelectorAll('.track-btn').forEach((btn, i) => {    btn.onclick = () => selectTrack(i);  });  // init  setPlaybackState('stopped');  setArm(ARM_PARKED);  genWave();  drawWave(0);</script>

Webentwicklung

Moderne, schnelle Websites & Web-Apps — von der Landing Page bis zum kompletten Auftritt.

Skripte & Automatisierung

Wiederkehrende Aufgaben automatisieren, APIs anbinden, Workflows optimieren.

Software & Tools

Individuelle Programme und Tools die genau das machen was du brauchst — nicht mehr, nicht weniger.

Referenzen

Beispiel-Websites

Demo-Projekte für fiktive Unternehmen – jedes in einem eigenen Stil. Wisch dich durch, oder öffne sie live.

01/02

Vorschau der Website Rechenwerk auf dem Desktop

Computerladen & Werkstatt

Rechenwerk

Hell, ruhig, schlicht

Reparaturschein mit Stempel, Preisrechner zum Umschalten, Emaille-Türschild mit Live-Öffnungszeiten. Gebaut ohne Animations-Bibliothek – schnell und leicht.

  • Astro
  • CSS
  • View Transitions
Vorschau der Website Hair Vision auf dem Desktop

Barbershop & Friseursalon

Hair Vision

Dunkel, edel, bewegt

Ein 3D-Kopf, dem beim Scrollen die Haare wachsen, Lookbook, Team-Seiten und weiche Übergänge. Viel Bewegung, klassische Typografie.

  • Astro
  • GSAP
  • Three.js
  • Tailwind
Videoproduktion

Bild & Video

Video ist für mich kein Add-on, sondern ein eigenes Handwerk: Musikvideos, cineastische Kurzfilme, Content für Artists und Brands. Gedreht wird mit eigener Kamera und professionellem Lichtequipment, von der ersten Idee bis zum fertigen Schnitt.

  • 01Musikvideos
  • 02Kurzfilme & Content
  • 03Skripte & Ideen
  • 04Schnitt & Color Grading
Pakete & Preise

Klare Preise, kein Rätselraten.

Startpreise für jeden Bereich. Was in den Paketen steckt, siehst du im Detail auf der Preisseite.

Alle Preise ansehen
Kontakt

Lass uns zusammenarbeiten

Egal ob Musikprojekt, Videodreh, Website oder IT-Lösung — schreib mir einfach.

oder

Deine Angaben nutze ich nur, um deine Anfrage zu beantworten. Mehr dazu in der Datenschutzerklärung.

Anfrage

Lass uns sprechen.

oder schreib mir hier

Deine Angaben nutze ich nur, um deine Anfrage zu beantworten. Mehr dazu in der Datenschutzerklärung.