Web Pages Hard
Red Portfolio
A high-velocity portfolio website featuring Three.js speed field background, scroll-reactive speed lines, and motion-first design with red and blue accents for fast-paced product teams.
Open in Lab
MCP
html css javascript threejs gsap lenis webgl
Targets: JS HTML
Code
:root {
color-scheme: dark;
--bg: #06070c;
--panel: rgba(12, 14, 22, 0.82);
--stroke: rgba(255, 255, 255, 0.1);
--text: #f5f2ef;
--muted: rgba(245, 242, 239, 0.6);
--red: #ff2e3a;
--red-soft: rgba(255, 46, 58, 0.2);
--blue: #6dd5ff;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
font-family: "Space Grotesk", sans-serif;
background: radial-gradient(circle at 10% 10%, rgba(255, 46, 58, 0.15), transparent 55%),
radial-gradient(circle at 80% 20%, rgba(109, 213, 255, 0.15), transparent 60%), #05060a;
color: var(--text);
overflow-x: hidden;
}
a {
color: inherit;
text-decoration: none;
}
#bg {
position: fixed;
inset: 0;
z-index: 0;
}
.speed-lines {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 1;
background: repeating-linear-gradient(
100deg,
rgba(255, 255, 255, 0.06) 0,
rgba(255, 255, 255, 0.06) 1px,
transparent 1px,
transparent 18px
);
opacity: 0.25;
transform: translateX(var(--speed-shift, 0px));
transition: opacity 0.4s ease;
}
.site-header {
position: sticky;
top: 0;
z-index: 5;
display: flex;
justify-content: space-between;
align-items: center;
padding: 24px clamp(20px, 6vw, 80px);
backdrop-filter: blur(14px);
background: rgba(5, 6, 10, 0.85);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.logo {
display: flex;
align-items: center;
gap: 16px;
}
.logo-mark {
width: 46px;
height: 46px;
border-radius: 14px;
background: linear-gradient(140deg, var(--red), #ff7a45);
box-shadow: 0 0 26px rgba(255, 46, 58, 0.45);
}
.logo-title {
font-family: "Bebas Neue", sans-serif;
font-size: 1.4rem;
letter-spacing: 0.1em;
margin: 0;
}
.logo-sub {
margin: 4px 0 0;
color: var(--muted);
font-size: 0.85rem;
}
.nav {
display: flex;
align-items: center;
gap: 20px;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.15em;
}
.nav a {
color: var(--muted);
}
.nav-cta {
padding: 0.5rem 1.2rem;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.2);
color: var(--text);
background: rgba(255, 46, 58, 0.15);
}
.page {
position: relative;
z-index: 2;
padding: 0 clamp(20px, 6vw, 80px) 80px;
}
.hero {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 36px;
padding: 64px 0 40px;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.3em;
color: var(--blue);
font-size: 0.7rem;
}
.hero-copy h1 {
font-family: "Bebas Neue", sans-serif;
font-size: clamp(2.8rem, 5vw, 4.5rem);
margin: 16px 0;
}
.lead {
color: var(--muted);
line-height: 1.6;
max-width: 60ch;
}
.hero-actions {
margin-top: 24px;
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.btn {
padding: 0.85rem 1.6rem;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.2);
background: transparent;
color: var(--text);
text-transform: uppercase;
letter-spacing: 0.2em;
font-size: 0.65rem;
}
.btn.primary {
background: rgba(255, 46, 58, 0.25);
border-color: rgba(255, 46, 58, 0.6);
}
.btn.ghost {
border-color: rgba(109, 213, 255, 0.6);
}
.hero-metrics {
margin-top: 24px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
}
.hero-metrics div {
background: var(--panel);
border: 1px solid var(--stroke);
border-radius: 14px;
padding: 14px;
}
.hero-metrics span {
display: block;
color: var(--muted);
font-size: 0.8rem;
}
.hero-panel {
display: grid;
gap: 18px;
}
.panel-card {
background: var(--panel);
border: 1px solid var(--stroke);
border-radius: 18px;
padding: 20px;
}
.panel-title {
text-transform: uppercase;
letter-spacing: 0.2em;
font-size: 0.65rem;
color: var(--muted);
}
.marquee {
margin: 24px 0 0;
overflow: hidden;
border-top: 1px solid rgba(255, 255, 255, 0.08);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.marquee-track {
display: flex;
gap: 48px;
padding: 14px 0;
text-transform: uppercase;
letter-spacing: 0.35em;
font-size: 0.7rem;
color: var(--muted);
white-space: nowrap;
will-change: transform;
}
.section {
padding: 56px 0;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 16px;
}
.section-header p {
color: var(--muted);
}
.grid {
margin-top: 24px;
display: grid;
gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.card {
background: var(--panel);
border: 1px solid var(--stroke);
border-radius: 16px;
padding: 20px;
}
.card span {
text-transform: uppercase;
letter-spacing: 0.2em;
font-size: 0.65rem;
color: var(--blue);
}
.card p {
color: var(--muted);
}
.split {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 24px;
}
.list {
list-style: none;
padding: 0;
margin: 18px 0 0;
display: grid;
gap: 12px;
}
.list li {
padding: 12px 16px;
border-radius: 12px;
background: rgba(12, 14, 20, 0.8);
border: 1px solid var(--stroke);
}
.pill-grid {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 18px;
}
.pill-grid span {
padding: 8px 14px;
border-radius: 999px;
border: 1px solid rgba(109, 213, 255, 0.5);
font-size: 0.75rem;
}
.stats {
display: grid;
gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.stats div {
background: var(--panel);
border: 1px solid var(--stroke);
border-radius: 16px;
padding: 20px;
text-align: center;
}
.stats h2 {
font-family: "Bebas Neue", sans-serif;
font-size: 2.4rem;
margin: 0 0 8px;
color: var(--red);
}
.lab-grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.lab-card {
padding: 18px;
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(8, 10, 16, 0.8);
}
.contact-cards {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.contact-card {
padding: 18px;
border-radius: 14px;
background: rgba(12, 14, 20, 0.85);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.contact-card p {
margin: 6px 0 0;
color: var(--muted);
}
@media (max-width: 900px) {
.site-header {
flex-direction: column;
align-items: flex-start;
}
.nav {
flex-wrap: wrap;
}
}
@media (max-width: 640px) {
.page {
padding: 0 18px 80px;
}
}const data = {
projects: [
{
title: "Pulse Atlas",
desc: "Realtime logistics map with motion-safe alerts.",
tag: "Dashboard",
},
{
title: "Vanta Ops",
desc: "Command UI for an autonomous fleet control room.",
tag: "Systems",
},
{
title: "Signal Forge",
desc: "Design system + motion kit for an AI platform.",
tag: "Design",
},
{
title: "Redline Launch",
desc: "Immersive product site with staggered cinematic scroll.",
tag: "Web",
},
],
capabilities: [
"Realtime dashboard UX",
"Motion systems and interaction design",
"Interface sound + haptic choreography",
"High-contrast product branding",
],
stack: ["Figma", "Three.js", "GSAP", "Lenis", "Framer", "WebGL"],
stats: [
{ value: "38", label: "Product launches" },
{ value: "11", label: "Design systems" },
{ value: "4", label: "Global teams" },
],
lab: [
{
title: "Velocity Ticker",
desc: "Scroll-driven marquee tied to Lenis momentum.",
},
{
title: "Speed Lines",
desc: "Background parallax reacting to scroll velocity.",
},
{
title: "Focus Beam",
desc: "Hover-triggered light sweeps across panels.",
},
],
contacts: [
{ label: "Email", value: "redline@studio.com" },
{ label: "LinkedIn", value: "/redline" },
{ label: "GitHub", value: "@redline" },
],
};
const projectGrid = document.getElementById("project-grid");
const capabilityList = document.getElementById("capabilities");
const stackGrid = document.getElementById("stack");
const statsGrid = document.getElementById("stats");
const labGrid = document.getElementById("lab-grid");
const contactGrid = document.getElementById("contact-cards");
if (projectGrid) {
projectGrid.innerHTML = data.projects
.map(
(project) => `
<article class="card">
<span>${project.tag}</span>
<h3>${project.title}</h3>
<p>${project.desc}</p>
</article>
`
)
.join("");
}
if (capabilityList) {
capabilityList.innerHTML = data.capabilities.map((item) => `<li>${item}</li>`).join("");
}
if (stackGrid) {
stackGrid.innerHTML = data.stack.map((item) => `<span>${item}</span>`).join("");
}
if (statsGrid) {
statsGrid.innerHTML = data.stats
.map(
(stat) => `
<div>
<h2>${stat.value}</h2>
<p>${stat.label}</p>
</div>
`
)
.join("");
}
if (labGrid) {
labGrid.innerHTML = data.lab
.map(
(item) => `
<article class="lab-card">
<h3>${item.title}</h3>
<p>${item.desc}</p>
</article>
`
)
.join("");
}
if (contactGrid) {
contactGrid.innerHTML = data.contacts
.map(
(contact) => `
<article class="contact-card">
<h4>${contact.label}</h4>
<p>${contact.value}</p>
</article>
`
)
.join("");
}
const lenis = window.Lenis
? new Lenis({
smoothWheel: true,
smoothTouch: false,
lerp: 0.08,
})
: null;
let scrollVelocity = 0;
let marqueeX = 0;
function raf(time) {
if (lenis) lenis.raf(time);
scrollVelocity *= 0.9;
document.documentElement.style.setProperty("--speed-shift", `${scrollVelocity * 14}px`);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
if (lenis) {
lenis.on("scroll", ({ velocity }) => {
scrollVelocity = Math.max(Math.min(velocity, 40), -40);
});
}
const marquee = document.getElementById("marqueeTrack");
if (marquee) {
const loop = () => {
marqueeX -= 0.4 + Math.abs(scrollVelocity) * 0.03;
marquee.style.transform = `translateX(${marqueeX}px)`;
if (Math.abs(marqueeX) > marquee.scrollWidth / 2) {
marqueeX = 0;
}
requestAnimationFrame(loop);
};
loop();
}
if (window.gsap) {
gsap.from(".hero-copy", { opacity: 0, y: 30, duration: 1, ease: "power3.out" });
gsap.from(".hero-panel", { opacity: 0, x: 30, duration: 1, delay: 0.2, ease: "power3.out" });
gsap.utils
.toArray(".card, .list li, .pill-grid span, .stats div, .lab-card")
.forEach((item, index) => {
gsap.from(item, {
opacity: 0,
y: 24,
duration: 0.8,
delay: index * 0.05,
ease: "power2.out",
});
});
}
class SpeedField {
constructor(container) {
this.container = container;
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 200);
this.camera.position.z = 30;
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
container.appendChild(this.renderer.domElement);
this.speed = 0.4;
this.lines = this.createLines();
this.scene.add(this.lines);
this.clock = new THREE.Clock();
this.bindEvents();
this.animate();
}
createLines() {
const count = 220;
const positions = new Float32Array(count * 2 * 3);
const colors = new Float32Array(count * 2 * 3);
for (let i = 0; i < count; i++) {
const x = (Math.random() - 0.5) * 60;
const y = (Math.random() - 0.5) * 40;
const z = -Math.random() * 120;
const length = 2 + Math.random() * 6;
const idx = i * 6;
positions[idx] = x;
positions[idx + 1] = y;
positions[idx + 2] = z;
positions[idx + 3] = x;
positions[idx + 4] = y;
positions[idx + 5] = z + length;
const color = new THREE.Color(0xff2e3a);
colors[idx] = color.r;
colors[idx + 1] = color.g;
colors[idx + 2] = color.b;
colors[idx + 3] = color.r;
colors[idx + 4] = color.g;
colors[idx + 5] = color.b;
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
const material = new THREE.LineBasicMaterial({
vertexColors: true,
transparent: true,
opacity: 0.35,
});
return new THREE.LineSegments(geometry, material);
}
updateLines(delta) {
const positions = this.lines.geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 6) {
positions[i + 2] += this.speed + Math.abs(scrollVelocity) * 0.04;
positions[i + 5] += this.speed + Math.abs(scrollVelocity) * 0.04;
if (positions[i + 2] > 10) {
const x = (Math.random() - 0.5) * 60;
const y = (Math.random() - 0.5) * 40;
const z = -120 - Math.random() * 40;
const length = 2 + Math.random() * 6;
positions[i] = x;
positions[i + 1] = y;
positions[i + 2] = z;
positions[i + 3] = x;
positions[i + 4] = y;
positions[i + 5] = z + length;
}
}
this.lines.geometry.attributes.position.needsUpdate = true;
}
bindEvents() {
window.addEventListener("resize", () => this.onResize());
}
onResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
animate() {
const delta = this.clock.getDelta();
this.updateLines(delta);
this.lines.rotation.z += delta * 0.02;
this.lines.rotation.y += delta * 0.01;
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(() => this.animate());
}
}
const bg = document.getElementById("bg");
if (bg && window.THREE) {
new SpeedField(bg);
}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Redline Drift Portfolio</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="bg"></div>
<div class="speed-lines" aria-hidden="true"></div>
<header class="site-header">
<div class="logo">
<span class="logo-mark"></span>
<div>
<p class="logo-title">Redline Drift</p>
<p class="logo-sub">Motion-first product designer</p>
</div>
</div>
<nav class="nav">
<a href="#work">Work</a>
<a href="#services">Services</a>
<a href="#lab">Lab</a>
<a href="#contact" class="nav-cta">Contact</a>
</nav>
</header>
<main class="page">
<section class="hero">
<div class="hero-copy">
<p class="eyebrow">Redline System 02</p>
<h1>High-velocity interfaces for teams that ship fast.</h1>
<p class="lead">
I design cinematic product experiences, motion systems, and real-time dashboards.
Lenis drives the smooth drift below, while a Three.js speed field powers the background.
</p>
<div class="hero-actions">
<button class="btn primary">Launch reel</button>
<button class="btn ghost">Download casebook</button>
</div>
<div class="hero-metrics">
<div>
<span>Velocity</span>
<strong>920 px/s</strong>
</div>
<div>
<span>Deploys</span>
<strong>38</strong>
</div>
<div>
<span>Signal</span>
<strong>99.2%</strong>
</div>
</div>
</div>
<div class="hero-panel">
<div class="panel-card">
<p class="panel-title">Current Focus</p>
<h3>Realtime UI systems</h3>
<p>Spatial dashboards, motion kits, and multi-surface UX for logistics + AI teams.</p>
</div>
<div class="panel-card">
<p class="panel-title">Availability</p>
<h3>May → August 2026</h3>
<p>Limited partnerships for high-intensity builds.</p>
</div>
</div>
</section>
<section class="marquee" aria-hidden="true">
<div class="marquee-track" id="marqueeTrack">
<span>Lenis Smooth Drift</span>
<span>Three.js Speed Field</span>
<span>GSAP Motion Passes</span>
<span>Redline Interface Lab</span>
</div>
</section>
<section class="section" id="work">
<div class="section-header">
<h2>Selected Work</h2>
<p>Systems built for clarity under pressure.</p>
</div>
<div class="grid" id="project-grid"></div>
</section>
<section class="section split" id="services">
<div>
<h2>Services</h2>
<ul class="list" id="capabilities"></ul>
</div>
<div>
<h2>Toolchain</h2>
<div class="pill-grid" id="stack"></div>
</div>
</section>
<section class="section stats" id="stats"></section>
<section class="section" id="lab">
<div class="section-header">
<h2>Lab Experiments</h2>
<p>Lenis-powered scroll modules.</p>
</div>
<div class="lab-grid" id="lab-grid"></div>
</section>
<section class="section" id="contact">
<div class="section-header">
<h2>Contact</h2>
<p>Ready to build in the fast lane.</p>
</div>
<div class="contact-cards" id="contact-cards"></div>
</section>
</main>
<script src="https://cdn.jsdelivr.net/npm/@studio-freight/lenis@1.0.42/bundled/lenis.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script src="script.js"></script>
</body>
</html>Red Portfolio
A high-velocity portfolio website designed for fast-paced product teams. Features a Three.js speed field background with animated lines, scroll-reactive speed effects, and motion-first design principles with red and blue color accents.
How it works
The portfolio emphasizes speed and motion:
- Three.js Speed Field — Animated line segments creating a speed tunnel effect
- Scroll Velocity Tracking — Speed lines react to scroll velocity
- Marquee Animation — Scroll-driven marquee that speeds up with momentum
- Motion Systems — GSAP animations for cinematic transitions
- High-Contrast Design — Red and blue accents for clarity and impact
Key features
- Three.js speed field with animated line segments
- Scroll-velocity reactive speed lines
- Animated marquee tied to scroll momentum
- High-contrast red and blue color scheme
- Motion-first design principles
- Smooth scroll with Lenis
- GSAP-powered animations
- Bebas Neue font for bold typography
Technical details
The Three.js speed field:
- Line segments that move based on scroll velocity
- Continuous animation loop with recycling
- Rotation effects for depth
- Color-coded red lines for visual impact
- Performance optimized with RAF
When to use it
- Product designer portfolios
- Motion-first brand experiences
- High-velocity team showcases
- Cinematic product presentations
- Fast-paced startup portfolios
- Motion design studios