DEV Community

Cover image for How I Built a Cyberpunk Portfolio with an AI Coding Agent (Without Technical Debt)
<A.BONFIGLIO/>
<A.BONFIGLIO/>

Posted on Originally published at bonfiglioalessio.github.io

How I Built a Cyberpunk Portfolio with an AI Coding Agent (Without Technical Debt)

Most conversations around AI coding today revolve around two extremes: either generating quick 50-line snippets or getting trapped in a cycle of messy, spaghetti code generated by over-enthusiastic LLMs.

When building the new version of my personal portfolio—a retro-futuristic, cyberpunk developer workstation—I set a different challenge: Can an autonomous AI coding agent (Google Antigravity / Gemini) act as a true engineering pair without introducing technical debt, degrading architecture, or cluttering the bundle?

Here is the complete engineering breakdown of how Spec-Driven Development, 0-Byte Asset procedural audio, and native browser APIs made this project lightning-fast, highly interactive, and completely open-source.

Portfolio Live Hero Overview


🧭 1. The Core Problem: The "AI Coding Trap"

Large Language Models excel at generating functional code quickly. However, when left unguided across multiple iterations, common issues quickly emerge:

  • Inconsistent coding conventions (mixing Options API with Composition API, loose any types).
  • Component bloat and duplicated UI logic.
  • Accidental regressions on existing features.
  • Phantom NPM dependencies installed for trivial tasks.

To solve this, I treated the AI agent not as a magical coder, but as an autonomous junior-to-mid developer executing within a strictly enforced architectural sandbox.


🛡️ 2. The Solution: Spec-Driven Development (AGENTS.md)

Before writing a single line of component code, I defined persistent governance guidelines directly in AGENTS.md and GEMINI.md:

# Architectural Rules & Development Standards

1. Stack: Vite 6 + Vue 3.5 (Composition API / `<script setup>`) + TypeScript (Strict) + Tailwind CSS + SCSS (`sass-embedded`).
2. Progressive Enhancement:
   - Phase 1: Robust HTML5 semantic structure, A11y, responsive design, base UI.
   - Phase 2: Canvas starfield, Web Audio SFX, 3D tilt, interactive shell CLI.
3. Centralized & Strictly Typed Data Layer: All copy, project lists, and skills are typed in `src/types/portfolio.ts` and managed in `src/data/portfolio.ts`. Zero hardcoded markup strings.
4. Collaboration Rules:
   - "Plan First, Code Later": AI must produce an implementation plan before writing code.
   - Zero unapproved edits: Every step is verified before proceeding.
   - Atomic Conventional Commits: Single-line English commits with automatic CHANGELOG generation.
Enter fullscreen mode Exit fullscreen mode

Whenever the agent proposed a new feature, it had to reference these rules. This simple file eliminated 95% of common AI hallucinations and kept the codebase consistent across 20+ feature branches.


⚡ 3. Deep Dive: The "0-Byte Asset" Procedural Audio Synth

One of the key technical goals was: zero external .mp3 or .wav sound files in the production bundle.

Instead of downloading hundreds of kilobytes of audio files, all sound effects (hover ticks, button clicks, CLI keypresses, modal open whooshes, warp speed re-entry, and an ambient background space drone) are procedurally synthesized in real time using the native browser Web Audio API.

Here is a snippet from our src/composables/useAudioSynth.ts powering the generative ambient drone in D minor add9 (frequencies: 73.41Hz, 110.0Hz, 174.61Hz, 220.0Hz, 329.63Hz):

// src/composables/useAudioSynth.ts
function startAmbientMusic() {
  if (isMusicPlaying) return;
  const ctx = getAudioContext();
  if (!ctx || !musicGain) return;

  isMusicPlaying = true;
  const now = ctx.currentTime;

  // Smooth fade-in of space ambient bed
  musicGain.gain.cancelScheduledValues(now);
  musicGain.gain.setValueAtTime(0.001, now);
  musicGain.gain.linearRampToValueAtTime(0.035, now + 2.0);

  // Cosmic Harmonic Drone: D minor add9
  const droneFreqs = [73.41, 110.0, 174.61, 220.0, 329.63];

  ambientOscs = droneFreqs.map((freq, idx) => {
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    const filter = ctx.createBiquadFilter();

    osc.type = idx % 2 === 0 ? 'sine' : 'triangle';
    osc.frequency.setValueAtTime(freq, now);

    // Warm low-pass filtering for a soft space atmospheric texture
    filter.type = 'lowpass';
    filter.frequency.setValueAtTime(450 + idx * 80, now);

    gain.gain.setValueAtTime(0.18 / (idx + 1), now);

    osc.connect(filter);
    filter.connect(gain);
    gain.connect(musicGain!);
    osc.start();
    return osc;
  });
}
Enter fullscreen mode Exit fullscreen mode

Micro-Interaction SFX: Procedural Keystroke & Quantum Warp

For the interactive CLI, each keystroke calculates random pitch jitter for realistic mechanical clatter:

export function playCliKeystroke() {
  const ctx = getAudioContext();
  if (!ctx || !sfxGain) return;

  const now = ctx.currentTime;
  const osc = ctx.createOscillator();
  const gain = ctx.createGain();

  // Subtle pitch jitter between 520Hz and 640Hz
  const freq = 520 + Math.random() * 120;
  osc.type = 'triangle';
  osc.frequency.setValueAtTime(freq, now);
  osc.frequency.exponentialRampToValueAtTime(140, now + 0.035);

  gain.gain.setValueAtTime(0.12, now);
  gain.gain.exponentialRampToValueAtTime(0.001, now + 0.035);

  osc.connect(gain);
  gain.connect(sfxGain);
  osc.start(now);
  osc.stop(now + 0.035);
}
Enter fullscreen mode Exit fullscreen mode

Total external audio payload downloaded: 0 KB.


🌌 4. Deep Dive: 60 FPS SpaceCanvas & 0% Background CPU

The background features an interactive 3D particle field that responds to mouse movement, creates dynamic constellation connections, and triggers supernova shockwaves upon clicking.

Performance Optimization: Squared Distance

Running Math.sqrt() on hundreds of particle combinations 60 times per second degrades frame rates. We optimized all connection calculations using squared Euclidean distance (dx² + dy²):

// Squared distance check: Avoids expensive Math.sqrt in the animation loop
const maxDistance = 120;
const maxDistanceSq = maxDistance * maxDistance;

for (let i = 0; i < particles.length; i++) {
  for (let j = i + 1; j < particles.length; j++) {
    const dx = particles[i].x - particles[j].x;
    const dy = particles[i].y - particles[j].y;
    const distSq = dx * dx + dy * dy;

    if (distSq < maxDistanceSq) {
      const alpha = 1 - (distSq / maxDistanceSq);
      ctx.strokeStyle = `rgba(56, 189, 248, ${alpha * 0.25})`;
      ctx.beginPath();
      ctx.moveTo(particles[i].x, particles[i].y);
      ctx.lineTo(particles[j].x, particles[j].y);
      ctx.stroke();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Zero Background Consumption with the Page Visibility API

To prevent laptops from draining battery when the browser tab is hidden in the background, we hook into document.visibilitychange:

function handleVisibilityChange() {
  if (document.hidden) {
    cancelAnimationFrame(animationFrameId);
    if (isAudioEnabled.value) stopAmbientMusic();
  } else {
    lastTimestamp = performance.now();
    animationFrameId = requestAnimationFrame(renderLoop);
    if (isAudioEnabled.value) startAmbientMusic();
  }
}
Enter fullscreen mode Exit fullscreen mode

Result: Exactly 0% CPU and 0% GPU usage when tab is inactive.


🖼️ 5. Multi-Device Live Iframe Sandbox

Rather than just showing static screenshots or linking away to external tabs, users can test and navigate real production projects directly inside an in-browser sandbox with a dynamic device switcher:

  • Desktop (1280px container)
  • Tablet (768px container)
  • Mobile (375px container)
<!-- Device switcher with dynamic CSS variables & responsive frame constraints -->
<div class="iframe-container" :style="{ width: activeDeviceWidth }">
  <iframe
    :src="project.liveUrl"
    loading="lazy"
    sandbox="allow-scripts allow-same-origin allow-popups"
    class="project-iframe"
  />
</div>
Enter fullscreen mode Exit fullscreen mode

🕹️ 6. The Secret Desktop Easter Egg Challenge!

As an interactive tribute to retro cyberpunk terminals and game developers, I hid a secret interactive feature exclusive to desktop browsers.

Can you find what combination or UI action triggers the Easter Egg?

⚠️ Rule: No peeking at the open-source GitHub source code to spoil the mystery! 😉

If you manage to discover it, let me know what happened in the comments below!


📊 Key Engineering Takeaways

  1. AI Agents need clear contracts, not longer prompts: An AGENTS.md file specifying strict conventions produces 10x better code than conversational prompt engineering.
  2. Leverage the Browser's native APIs: Web Audio API, Canvas 2D, and Page Visibility API let you build AAA-feeling experiences without ballooning your bundle size with third-party libraries.
  3. Keep the data layer decoupled: Separating data (src/data/portfolio.ts) from components allows instant copy changes without touching UI templates.

🔗 Try It Live & Explore the Code


💬 Discussion

What are your thoughts on Spec-Driven Development with AI? Have you experimented with persistent markdown rules (AGENTS.md) in your projects? Let's talk in the comments!

Top comments (0)