<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dr. J vs. Larry Bird | One on One: The Ultimate Rivalry & Arcade</title>
<meta name="description" content="Experience the legendary rivalry between Julius 'Dr. J' Erving and Larry Bird. Playable 1-on-1 basketball arcade game, stats comparison, career retrospective, and 1983 EA video game homage.">
<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=Inter:wght@300;400;600;700;800&family=Press+Start+2P&display=swap" rel="stylesheet">
<style>
:root {
--bg-dark: #0a0d14;
--bg-card: rgba(18, 24, 38, 0.78);
--bg-glass: rgba(255, 255, 255, 0.05);
--border-glass: rgba(255, 255, 255, 0.12);
/* Team Colors */
--celtics-green: #008348;
--celtics-glow: rgba(0, 131, 72, 0.6);
--celtics-gold: #f8c924;
--sixers-red: #ed174c;
--sixers-glow: rgba(237, 23, 76, 0.6);
--sixers-blue: #006bb6;
--sixers-light-blue: #00a2ff;
--text-main: #f3f4f6;
--text-dim: #9ca3af;
--text-highlight: #ffffff;
--hardwood: #cb8b47;
--hardwood-lines: #f5eedc;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
-webkit-user-select: none;
user-select: none;
}
body {
background-color: var(--bg-dark);
background-image:
radial-gradient(circle at 15% 20%, rgba(237, 23, 76, 0.15) 0%, transparent 45%),
radial-gradient(circle at 85% 20%, rgba(0, 131, 72, 0.15) 0%, transparent 45%),
radial-gradient(circle at 50% 80%, rgba(0, 107, 182, 0.1) 0%, transparent 50%),
linear-gradient(to bottom, #090c13 0%, #0d121c 100%);
color: var(--text-main);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
min-height: 100vh;
overflow-x: hidden;
line-height: 1.5;
}
/* Top Navigation / Header Bar */
header {
border-bottom: 1px solid var(--border-glass);
background: rgba(10, 13, 20, 0.85);
backdrop-filter: blur(16px);
position: sticky;
top: 0;
z-index: 100;
padding: 0.85rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.brand {
display: flex;
align-items: center;
gap: 12px;
}
.brand-badge {
font-family: 'Press Start 2P', monospace;
font-size: 0.6rem;
background: linear-gradient(135deg, var(--sixers-red), var(--celtics-green));
padding: 4px 8px;
border-radius: 4px;
color: #fff;
letter-spacing: 1px;
box-shadow: 0 0 12px rgba(237, 23, 76, 0.4);
}
.brand-title {
font-family: 'Bebas Neue', cursive;
font-size: 1.7rem;
letter-spacing: 2px;
background: linear-gradient(90deg, #ff4d6d, #ffffff 50%, #2ecc71);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.nav-tabs {
display: flex;
background: rgba(255, 255, 255, 0.05);
padding: 4px;
border-radius: 30px;
border: 1px solid var(--border-glass);
gap: 4px;
}
.nav-btn {
background: transparent;
border: none;
color: var(--text-dim);
font-weight: 600;
font-size: 0.85rem;
padding: 8px 18px;
border-radius: 20px;
cursor: pointer;
transition: all 0.25s ease;
display: flex;
align-items: center;
gap: 6px;
}
.nav-btn:hover {
color: #fff;
background: rgba(255, 255, 255, 0.08);
}
.nav-btn.active {
color: #fff;
background: linear-gradient(135deg, rgba(237, 23, 76, 0.8), rgba(0, 131, 72, 0.8));
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}
.quick-actions {
display: flex;
gap: 10px;
align-items: center;
}
.btn-icon {
background: rgba(255, 255, 255, 0.07);
border: 1px solid var(--border-glass);
color: var(--text-main);
width: 38px;
height: 38px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
font-size: 1rem;
}
.btn-icon:hover {
background: rgba(255, 255, 255, 0.15);
transform: scale(1.05);
}
/* Main Container */
main {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
}
/* Hero Matchup Banner */
.hero-banner {
position: relative;
border-radius: 24px;
overflow: hidden;
margin-bottom: 2.5rem;
border: 1px solid var(--border-glass);
background: linear-gradient(135deg, rgba(237, 23, 76, 0.12) 0%, rgba(10, 13, 20, 0.95) 50%, rgba(0, 131, 72, 0.12) 100%);
box-shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.7);
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
padding: 2.5rem 3rem;
min-height: 280px;
}
.hero-banner::before {
content: "";
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 1px;
height: 100%;
background: linear-gradient(to bottom, transparent, var(--border-glass), transparent);
}
.player-card-hero {
display: flex;
flex-direction: column;
z-index: 2;
}
.player-card-hero.sixers {
text-align: left;
align-items: flex-start;
}
.player-card-hero.celtics {
text-align: right;
align-items: flex-end;
}
.player-sub {
font-family: 'Bebas Neue', cursive;
font-size: 1.15rem;
letter-spacing: 2px;
text-transform: uppercase;
margin-bottom: 4px;
}
.sixers .player-sub {
color: #ff6b8b;
}
.celtics .player-sub {
color: #4cd988;
}
.player-name {
font-family: 'Bebas Neue', cursive;
font-size: 3.8rem;
line-height: 0.95;
letter-spacing: 2px;
text-shadow: 0 0 25px rgba(0,0,0,0.8);
}
.sixers .player-name {
background: linear-gradient(to right, #fff, #ff8da6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.celtics .player-name {
background: linear-gradient(to left, #fff, #84e1aa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.player-tags {
display: flex;
gap: 8px;
margin-top: 12px;
flex-wrap: wrap;
}
.tag {
font-size: 0.72rem;
font-weight: 700;
padding: 4px 10px;
border-radius: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.sixers .tag {
background: rgba(237, 23, 76, 0.25);
border: 1px solid rgba(237, 23, 76, 0.4);
color: #ff99b0;
}
.celtics .tag {
background: rgba(0, 131, 72, 0.25);
border: 1px solid rgba(0, 131, 72, 0.4);
color: #79e5a7;
}
.vs-emblem {
z-index: 3;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 2rem;
}
.vs-circle {
width: 76px;
height: 76px;
border-radius: 50%;
background: #111520;
border: 2px solid var(--border-glass);
box-shadow: 0 0 30px rgba(0, 0, 0, 0.8), 0 0 15px rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
justify-content: center;
font-family: 'Bebas Neue', cursive;
font-size: 2.2rem;
color: #f3f4f6;
letter-spacing: 1px;
position: relative;
}
.vs-circle::before, .vs-circle::after {
content: "";
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
}
.vs-circle::before { left: -14px; background: var(--sixers-red); box-shadow: 0 0 10px var(--sixers-red); }
.vs-circle::after { right: -14px; background: var(--celtics-green); box-shadow: 0 0 10px var(--celtics-green); }
.vs-label {
margin-top: 10px;
font-family: 'Press Start 2P', monospace;
font-size: 0.55rem;
color: #f1c40f;
letter-spacing: 2px;
text-transform: uppercase;
text-align: center;
opacity: 0.9;
}
/* Tab Content Panels */
.tab-panel {
display: none;
animation: fadeIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.tab-panel.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* -------------------------------------------------------------
SECTION 1: ARCADE 1-ON-1 COURT (CANVAS GAME)
------------------------------------------------------------- */
.arcade-container {
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 24px;
padding: 1.5rem;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5);
position: relative;
}
.arcade-hud {
display: flex;
justify-content: space-between;
align-items: center;
background: #0d111a;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
padding: 12px 24px;
margin-bottom: 1.2rem;
}
.hud-team {
display: flex;
align-items: center;
gap: 16px;
}
.hud-avatar {
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Bebas Neue', cursive;
font-size: 1.6rem;
font-weight: bold;
color: #fff;
}
.avatar-erving {
background: linear-gradient(135deg, var(--sixers-red), var(--sixers-blue));
border: 2px solid #ff4d6d;
}
.avatar-bird {
background: linear-gradient(135deg, var(--celtics-green), #004d2a);
border: 2px solid #38ef7d;
}
.hud-info h4 {
font-size: 1.1rem;
font-weight: 700;
letter-spacing: 0.5px;
}
.hud-info span {
font-size: 0.75rem;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 1px;
}
.hud-score {
font-family: 'Press Start 2P', monospace;
font-size: 1.9rem;
color: #fff;
text-shadow: 0 0 15px rgba(255, 255, 255, 0.4);
min-width: 50px;
text-align: center;
}
.hud-center {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.hud-timer-box {
background: #000;
border: 2px solid #334155;
padding: 6px 18px;
border-radius: 8px;
text-align: center;
}
.hud-timer {
font-family: 'Press Start 2P', monospace;
font-size: 1.3rem;
color: #ff3344;
text-shadow: 0 0 10px rgba(255, 51, 68, 0.7);
}
.shot-clock {
font-family: 'Press Start 2P', monospace;
font-size: 0.8rem;
color: #f1c40f;
}
.court-wrapper {
position: relative;
width: 100%;
border-radius: 16px;
overflow: hidden;
border: 2px solid #374151;
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.8), 0 10px 25px rgba(0, 0, 0, 0.6);
background: #111;
}
#basketballCourt {
display: block;
width: 100%;
height: 520px;
cursor: crosshair;
}
.arcade-banner-alert {
position: absolute;
top: 24px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.85);
border: 2px solid #f1c40f;
padding: 10px 28px;
border-radius: 12px;
font-family: 'Press Start 2P', monospace;
font-size: 0.85rem;
color: #f8c924;
letter-spacing: 1px;
box-shadow: 0 0 25px rgba(248, 201, 36, 0.5);
pointer-events: none;
opacity: 0;
transition: opacity 0.2s, transform 0.2s;
z-index: 10;
text-align: center;
}
.arcade-banner-alert.visible {
opacity: 1;
transform: translateX(-50%) translateY(5px);
}
.court-controls-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1.2rem;
flex-wrap: wrap;
gap: 15px;
background: rgba(255, 255, 255, 0.03);
padding: 12px 18px;
border-radius: 12px;
border: 1px solid var(--border-glass);
}
.instructions-group {
display: flex;
gap: 18px;
align-items: center;
flex-wrap: wrap;
font-size: 0.8rem;
}
.key-badge {
display: inline-block;
background: #1e293b;
border: 1px solid #475569;
border-bottom: 2px solid #64748b;
color: #f8fafc;
padding: 3px 8px;
border-radius: 6px;
font-weight: 700;
font-family: monospace;
font-size: 0.75rem;
margin-right: 4px;
}
.arcade-actions {
display: flex;
gap: 10px;
}
.btn-arcade {
background: linear-gradient(135deg, #1f2937, #111827);
border: 1px solid #374151;
color: #f3f4f6;
padding: 8px 16px;
border-radius: 8px;
font-weight: 700;
font-size: 0.82rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
}
.btn-arcade:hover {
background: linear-gradient(135deg, #374151, #1f2937);
border-color: #64748b;
color: #fff;
}
.btn-arcade.primary {
background: linear-gradient(135deg, var(--sixers-red), #b91c1c);
border-color: #f87171;
}
.btn-arcade.primary:hover {
box-shadow: 0 0 15px rgba(237, 23, 76, 0.5);
}
/* -------------------------------------------------------------
SECTION 2: TALE OF THE TAPE & STATS COMPARISON
------------------------------------------------------------- */
.comparison-grid {
display: grid;
grid-template-columns: 1fr 1.2fr 1fr;
gap: 1.5rem;
align-items: start;
}
.bio-column {
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 20px;
padding: 1.8rem;
position: relative;
overflow: hidden;
}
.bio-column.dr-j {
border-top: 4px solid var(--sixers-red);
}
.bio-column.bird {
border-top: 4px solid var(--celtics-green);
}
.bio-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 1.5rem;
}
.bio-number {
font-family: 'Bebas Neue', cursive;
font-size: 2.8rem;
line-height: 1;
color: rgba(255, 255, 255, 0.15);
font-weight: bold;
}
.bio-title-wrap h3 {
font-family: 'Bebas Neue', cursive;
font-size: 1.8rem;
letter-spacing: 1px;
line-height: 1.1;
}
.bio-title-wrap p {
font-size: 0.8rem;
color: var(--text-dim);
}
.profile-meta-list {
list-style: none;
display: flex;
flex-direction: column;
gap: 12px;
font-size: 0.88rem;
}
.profile-meta-list li {
display: flex;
justify-content: space-between;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
padding-bottom: 8px;
}
.meta-lbl {
color: var(--text-dim);
}
.meta-val {
font-weight: 600;
color: #fff;
}
/* Radar / Stat Bars Center Column */
.stats-center-card {
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 20px;
padding: 1.8rem;
}
.stats-header-center {
text-align: center;
margin-bottom: 1.5rem;
}
.stats-header-center h3 {
font-family: 'Bebas Neue', cursive;
font-size: 2rem;
letter-spacing: 1.5px;
}
.stats-header-center p {
font-size: 0.8rem;
color: var(--text-dim);
}
.stat-bars-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.stat-row {
display: flex;
flex-direction: column;
gap: 6px;
}
.stat-info {
display: flex;
justify-content: space-between;
font-size: 0.82rem;
font-weight: 600;
}
.stat-info .j-score {
color: #ff6b8b;
}
.stat-info .stat-name {
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.8px;
font-size: 0.75rem;
}
.stat-info .bird-score {
color: #4cd988;
}
.dual-meter-track {
height: 10px;
background: #182030;
border-radius: 6px;
display: flex;
overflow: hidden;
position: relative;
}
.dual-meter-midline {
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 2px;
background: #374151;
z-index: 2;
}
.meter-fill-left {
background: linear-gradient(to left, var(--sixers-red), #b91c1c);
height: 100%;
width: 50%;
margin-left: auto;
border-radius: 4px 0 0 4px;
transition: width 1s ease;
}
.meter-fill-right {
background: linear-gradient(to right, var(--celtics-green), #059669);
height: 100%;
width: 50%;
margin-right: auto;
border-radius: 0 4px 4px 0;
transition: width 1s ease;
}
/* Head-to-Head Record Box */
.h2h-record-card {
margin-top: 1.5rem;
background: rgba(0, 0, 0, 0.4);
border: 1px solid var(--border-glass);
border-radius: 14px;
padding: 1rem;
text-align: center;
}
.h2h-title {
font-family: 'Press Start 2P', monospace;
font-size: 0.6rem;
color: #f1c40f;
margin-bottom: 8px;
letter-spacing: 1px;
}
.h2h-tally {
display: flex;
justify-content: space-around;
align-items: center;
font-family: 'Bebas Neue', cursive;
font-size: 1.7rem;
}
.h2h-tally span {
display: block;
font-size: 0.7rem;
font-family: 'Inter', sans-serif;
color: var(--text-dim);
font-weight: normal;
}
/* -------------------------------------------------------------
SECTION 3: 1983 EA RETRO HOMAGE & HISTORIC CLASHES
------------------------------------------------------------- */
.history-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
}
.card-dark {
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 20px;
padding: 1.8rem;
}
.retro-badge-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.retro-pill {
font-family: 'Press Start 2P', monospace;
font-size: 0.6rem;
background: #f1c40f;
color: #000;
padding: 4px 8px;
border-radius: 4px;
font-weight: bold;
}
.card-title {
font-family: 'Bebas Neue', cursive;
font-size: 2rem;
letter-spacing: 1px;
margin-bottom: 0.5rem;
}
.lead-text {
color: var(--text-dim);
font-size: 0.92rem;
margin-bottom: 1.2rem;
}
/* Timeline */
.timeline {
display: flex;
flex-direction: column;
gap: 1.2rem;
border-left: 2px solid rgba(255, 255, 255, 0.1);
padding-left: 1.2rem;
margin-left: 0.5rem;
}
.timeline-item {
position: relative;
}
.timeline-item::before {
content: "";
position: absolute;
left: -1.5rem;
top: 4px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #f1c40f;
box-shadow: 0 0 10px #f1c40f;
}
.timeline-year {
font-family: 'Bebas Neue', cursive;
font-size: 1.3rem;
color: #f1c40f;
letter-spacing: 1px;
}
.timeline-title {
font-weight: 700;
font-size: 0.95rem;
margin-bottom: 4px;
}
.timeline-desc {
font-size: 0.85rem;
color: var(--text-dim);
line-height: 1.4;
}
/* Quotes Carousel/Cards */
.quotes-grid {
display: flex;
flex-direction: column;
gap: 1rem;
}
.quote-card {
background: rgba(0, 0, 0, 0.35);
border: 1px solid rgba(255, 255, 255, 0.08);
border-left: 4px solid #f1c40f;
padding: 1.1rem;
border-radius: 8px;
}
.quote-card.erving {
border-left-color: var(--sixers-red);
}
.quote-card.bird {
border-left-color: var(--celtics-green);
}
.quote-text {
font-style: italic;
font-size: 0.9rem;
margin-bottom: 8px;
color: #e2e8f0;
}
.quote-author {
font-size: 0.78rem;
font-weight: 700;
color: var(--text-dim);
}
/* -------------------------------------------------------------
SECTION 4: INTERACTIVE TRIVIA / CLUTCH SHOOTOUT
------------------------------------------------------------- */
.quiz-card {
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 20px;
padding: 2rem;
margin-top: 2rem;
text-align: center;
}
.quiz-options {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin: 1.5rem 0;
}
.quiz-btn {
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--border-glass);
color: #fff;
padding: 14px;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
text-align: left;
}
.quiz-btn:hover {
background: rgba(255, 255, 255, 0.12);
transform: translateY(-2px);
}
.quiz-btn.correct {
background: rgba(0, 131, 72, 0.4);
border-color: var(--celtics-green);
box-shadow: 0 0 15px rgba(0, 131, 72, 0.4);
}
.quiz-btn.wrong {
background: rgba(237, 23, 76, 0.4);
border-color: var(--sixers-red);
}
/* CRT Scanline Effect (Toggleable) */
.scanlines-overlay {
pointer-events: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: linear-gradient(
rgba(18, 16, 16, 0) 50%,
rgba(0, 0, 0, 0.25) 50%
), linear-gradient(
90deg,
rgba(255, 0, 0, 0.03),
rgba(0, 255, 0, 0.01),
rgba(0, 0, 255, 0.03)
);
background-size: 100% 4px, 6px 100%;
z-index: 999;
opacity: 0;
transition: opacity 0.3s;
}
body.crt-active .scanlines-overlay {
opacity: 1;
}
/* Footer */
footer {
text-align: center;
padding: 3rem 1rem;
border-top: 1px solid var(--border-glass);
margin-top: 4rem;
color: var(--text-dim);
font-size: 0.85rem;
}
footer a {
color: #fff;
text-decoration: none;
}
/* Responsive */
@media (max-width: 900px) {
.hero-banner {
grid-template-columns: 1fr;
gap: 2rem;
text-align: center;
padding: 2rem 1.5rem;
}
.player-card-hero.sixers, .player-card-hero.celtics {
text-align: center;
align-items: center;
}
.vs-circle {
margin: 0 auto;
}
.comparison-grid, .history-grid, .quiz-options {
grid-template-columns: 1fr;
}
.court-controls-bar {
flex-direction: column;
align-items: flex-start;
}
}
</style>
</head>
<body>
<!-- Retro CRT Filter Overlay -->
<div class="scanlines-overlay" id="crtOverlay"></div>
<!-- Header -->
<header>
<div class="brand">
<span class="brand-badge">EA 1983 HOMAGE</span>
<span class="brand-title">ONE ON ONE: DR. J vs. LARRY BIRD</span>
</div>
<nav class="nav-tabs">
<button class="nav-btn active" onclick="switchTab('arcade')">
<span>🕹️</span> Play Arcade
</button>
<button class="nav-btn" onclick="switchTab('stats')">
<span>📊</span> Tale of the Tape
</button>
<button class="nav-btn" onclick="switchTab('history')">
<span>📜</span> Rivalry Archive
</button>
<button class="nav-btn" onclick="switchTab('trivia')">
<span>🏆</span> Trivia Challenge
</button>
</nav>
<div class="quick-actions">
<button class="btn-icon" id="soundToggleBtn" title="Toggle Sound FX" onclick="toggleAudio()">🔊</button>
<button class="btn-icon" id="crtToggleBtn" title="Toggle 1983 CRT Retro Scanlines" onclick="toggleCRT()">📺</button>
</div>
</header>
<main>
<!-- Hero Banner -->
<section class="hero-banner">
<div class="player-card-hero sixers">
<span class="player-sub">Philadelphia 76ers • #6 • Forward</span>
<h1 class="player-name">JULIUS ERVING</h1>
<p style="color: var(--text-dim); font-size: 0.9rem; max-width: 320px;">
"The Doctor" — The airborne pioneer who revolutionized basketball above the rim with elegance, soaring dunks, and iconic grace.
</p>
<div class="player-tags">
<span class="tag">4× MVP (NBA/ABA)</span>
<span class="tag">3× Champion</span>
<span class="tag">Dunk God</span>
</div>
</div>
<div class="vs-emblem">
<div class="vs-circle">VS</div>
<span class="vs-label">BOSTON GARDEN & THE SPECTRUM</span>
</div>
<div class="player-card-hero celtics">
<span class="player-sub">Boston Celtics • #33 • Forward</span>
<h2 class="player-name">LARRY BIRD</h2>
<p style="color: var(--text-dim); font-size: 0.9rem; max-width: 320px;">
"Larry Legend" — The ruthless sharpshooter and basketball savant with peerless court vision, lethal clutch shooting, and legendary trash talk.
</p>
<div class="player-tags">
<span class="tag">3× NBA MVP</span>
<span class="tag">3× Champion</span>
<span class="tag">Clutch King</span>
</div>
</div>
</section>
<!-- TAB 1: ARCADE 1-ON-1 BASKETBALL GAME -->
<section id="tab-arcade" class="tab-panel active">
<div class="arcade-container">
<!-- Arcade Scoreboard HUD -->
<div class="arcade-hud">
<div class="hud-team">
<div class="hud-avatar avatar-erving">#6</div>
<div class="hud-info">
<h4>Dr. J (Erving)</h4>
<span>Philadelphia 76ers</span>
</div>
<div class="hud-score" id="scoreErving">00</div>
</div>
<div class="hud-center">
<div class="hud-timer-box">
<div class="hud-timer" id="gameTimer">01:30</div>
</div>
<div class="shot-clock">SHOT CLOCK: <span id="shotClock">24</span>s</div>
</div>
<div class="hud-team" style="flex-direction: row-reverse;">
<div class="hud-avatar avatar-bird">#33</div>
<div class="hud-info" style="text-align: right;">
<h4>Larry Bird</h4>
<span>Boston Celtics</span>
</div>
<div class="hud-score" id="scoreBird">00</div>
</div>
</div>
<!-- Canvas Court Area -->
<div class="court-wrapper">
<canvas id="basketballCourt" width="960" height="520"></canvas>
<div class="arcade-banner-alert" id="gameAlertBanner">SLIDING DUNK! SHATTERED GLASS!</div>
</div>
<!-- Arcade Controls & Tips -->
<div class="court-controls-bar">
<div class="instructions-group">
<div><span class="key-badge">W</span><span class="key-badge">A</span><span class="key-badge">S</span><span class="key-badge">D</span> or <span class="key-badge">Arrows</span> Move</div>
<div><span class="key-badge">SPACE</span> Hold & Release to Shoot / Drive to Dunk</div>
<div><span class="key-badge">SHIFT</span> Steal / Contest Shot</div>
</div>
<div class="arcade-actions">
<button class="btn-arcade" onclick="switchPlayerControlled()">
<span id="playerRoleIcon">🔄</span> Switch: <strong id="currentControlledLabel">Dr. J (You)</strong>
</button>
<button class="btn-arcade" onclick="resetGameMatch()">
<span>🔄</span> Restart Game
</button>
<button class="btn-arcade primary" onclick="triggerShatteredGlassTest()">
<span>💥</span> Shatter Backboard!
</button>
</div>
</div>
</div>
</section>
<!-- TAB 2: TALE OF THE TAPE & STATS COMPARISON -->
<section id="tab-stats" class="tab-panel">
<div class="comparison-grid">
<!-- Dr. J Profile -->
<div class="bio-column dr-j">
<div class="bio-header">
<div class="bio-number">#6</div>
<div class="bio-title-wrap">
<h3>Julius "Dr. J" Erving</h3>
<p>The ABA & NBA Phenom</p>
</div>
</div>
<ul class="profile-meta-list">
<li><span class="meta-lbl">Height / Weight</span><span class="meta-val">6' 7" / 210 lbs</span></li>
<li><span class="meta-lbl">Wingspan</span><span class="meta-val">7' 3" (Massive Hands)</span></li>
<li><span class="meta-lbl">Career Points</span><span class="meta-val">30,026 (NBA + ABA)</span></li>
<li><span class="meta-lbl">Career PPG</span><span class="meta-val">24.2 PPG</span></li>
<li><span class="meta-lbl">Career Rebounds</span><span class="meta-val">8.5 RPG</span></li>
<li><span class="meta-lbl">Career Assists</span><span class="meta-val">4.2 APG</span></li>
<li><span class="meta-lbl">Steals + Blocks</span><span class="meta-val">2.0 SPG / 1.7 BPG</span></li>
<li><span class="meta-lbl">All-Star Selections</span><span class="meta-val">16× All-Star</span></li>
<li><span class="meta-lbl">Hall of Fame</span><span class="meta-val">Inducted 1993</span></li>
<li><span class="meta-lbl">Signature Move</span><span class="meta-val">Rock-the-Baby Dunk, Baseline Scoop</span></li>
</ul>
</div>
<!-- Center Comparison Matrix -->
<div class="stats-center-card">
<div class="stats-header-center">
<h3>HEAD-TO-HEAD ATTRIBUTES</h3>
<p>Evaluating their primes (1979 - 1987)</p>
</div>
<div class="stat-bars-container">
<div class="stat-row">
<div class="stat-info">
<span class="j-score">99</span>
<span class="stat-name">Vertical & Dunking</span>
<span class="bird-score">72</span>
</div>
<div class="dual-meter-track">
<div class="dual-meter-midline"></div>
<div class="meter-fill-left" style="width: 50%;"></div>
<div class="meter-fill-right" style="width: 36%;"></div>
</div>
</div>
<div class="stat-row">
<div class="stat-info">
<span class="j-score">70</span>
<span class="stat-name">3-Point Shooting</span>
<span class="bird-score">98</span>
</div>
<div class="dual-meter-track">
<div class="dual-meter-midline"></div>
<div class="meter-fill-left" style="width: 35%;"></div>
<div class="meter-fill-right" style="width: 49%;"></div>
</div>
</div>
<div class="stat-row">
<div class="stat-info">
<span class="j-score">82</span>
<span class="stat-name">Playmaking & Vision</span>
<span class="bird-score">98</span>
</div>
<div class="dual-meter-track">
<div class="dual-meter-midline"></div>
<div class="meter-fill-left" style="width: 41%;"></div>
<div class="meter-fill-right" style="width: 49%;"></div>
</div>
</div>
<div class="stat-row">
<div class="stat-info">
<span class="j-score">94</span>
<span class="stat-name">Transition Offense</span>
<span class="bird-score">86</span>
</div>
<div class="dual-meter-track">
<div class="dual-meter-midline"></div>
<div class="meter-fill-left" style="width: 47%;"></div>
<div class="meter-fill-right" style="width: 43%;"></div>
</div>
</div>
<div class="stat-row">
<div class="stat-info">
<span class="j-score">88</span>
<span class="stat-name">Perimeter Defense</span>
<span class="bird-score">86</span>
</div>
<div class="dual-meter-track">
<div class="dual-meter-midline"></div>
<div class="meter-fill-left" style="width: 44%;"></div>
<div class="meter-fill-right" style="width: 43%;"></div>
</div>
</div>
<div class="stat-row">
<div class="stat-info">
<span class="j-score">90</span>
<span class="stat-name">Clutch & Swagger</span>
<span class="bird-score">99</span>
</div>
<div class="dual-meter-track">
<div class="dual-meter-midline"></div>
<div class="meter-fill-left" style="width: 45%;"></div>
<div class="meter-fill-right" style="width: 50%;"></div>
</div>
</div>
</div>
<!-- Official Head-to-Head Record -->
<div class="h2h-record-card">
<div class="h2h-title">OFFICIAL NBA HEAD-TO-HEAD MEETINGS</div>
<div class="h2h-tally">
<div style="color: #ff6b8b;">
18 WINS
<span>Dr. J (Sixers)</span>
</div>
<div style="color: #9ca3af; font-size: 1.2rem;">
36 GAMES
<span>Total Matches</span>
</div>
<div style="color: #4cd988;">
18 WINS
<span>Bird (Celtics)</span>
</div>
</div>
<p style="font-size: 0.75rem; color: #9ca3af; margin-top: 8px;">
An unbelievable exact 18-18 regular season split! In playoff games, Bird edged Erving 19–13 across 5 epic playoff series.
</p>
</div>
</div>
<!-- Larry Bird Profile -->
<div class="bio-column bird">
<div class="bio-header" style="flex-direction: row-reverse; text-align: right;">
<div class="bio-number">#33</div>
<div class="bio-title-wrap">
<h3>Larry "The Legend" Bird</h3>
<p>The Hick from French Lick</p>
</div>
</div>
<ul class="profile-meta-list">
<li><span class="meta-lbl">Height / Weight</span><span class="meta-val">6' 9" / 220 lbs</span></li>
<li><span class="meta-lbl">Wingspan</span><span class="meta-val">6' 10" (Deadly Release)</span></li>
<li><span class="meta-lbl">Career Points</span><span class="meta-val">21,791 (NBA)</span></li>
<li><span class="meta-lbl">Career PPG</span><span class="meta-val">24.3 PPG</span></li>
<li><span class="meta-lbl">Career Rebounds</span><span class="meta-val">10.0 RPG</span></li>
<li><span class="meta-lbl">Career Assists</span><span class="meta-val">6.3 APG</span></li>
<li><span class="meta-lbl">Steals + Blocks</span><span class="meta-val">1.7 SPG / 0.8 BPG</span></li>
<li><span class="meta-lbl">All-Star Selections</span><span class="meta-val">12× All-Star</span></li>
<li><span class="meta-lbl">Hall of Fame</span><span class="meta-val">Inducted 1998</span></li>
<li><span class="meta-lbl">Signature Move</span><span class="meta-val">Fadeaway Dagger, Left-Hand Floater</span></li>
</ul>
</div>
</div>
</section>
<!-- TAB 3: RIVALRY ARCHIVE & 1983 EA HOMAGE -->
<section id="tab-history" class="tab-panel">
<div class="history-grid">
<!-- 1983 Video Game Impact -->
<div class="card-dark">
<div class="retro-badge-header">
<span class="retro-pill">HISTORIC GAMING MILESTONE</span>
<span style="font-size: 0.75rem; color: #f1c40f;">ELECTRONIC ARTS (1983)</span>
</div>
<h2 class="card-title">ONE ON ONE: DR. J VS. LARRY BIRD</h2>
<p class="lead-text">
Programmed by Eric Hammond and conceived by EA founder Trip Hawkins, this title changed sports gaming forever:
</p>
<div class="timeline">
<div class="timeline-item">
<div class="timeline-year">1983</div>
<div class="timeline-title">First Celebrity Licensed Video Game</div>
<div class="timeline-desc">EA secured the real names and likenesses of both superstars, paying them $25,000 each plus 2.5% royalties.</div>
</div>
<div class="timeline-item">
<div class="timeline-year">SHATTER!</div>
<div class="timeline-title">The Broken Backboard & The Janitor</div>
<div class="timeline-desc">If a player slammed a ferocious dunk, the backboard shattered into glittering pixels. A disgruntled janitor walked out grumbling to sweep up the shards!</div>
</div>
<div class="timeline-item">
<div class="timeline-year">AUTHENTICITY</div>
<div class="timeline-title">True Player Physics & Styles</div>
<div class="timeline-desc">Dr. J was faster and soared for high-flying reverse layups; Larry Bird had superior perimeter range and turnaround step-backs.</div>
</div>
</div>
</div>
<!-- The Real Rivalry & Legendary Moments -->
<div class="card-dark">
<div class="retro-badge-header">
<span class="retro-pill" style="background:#ed174c; color:#fff;">BLOOD & GLORY</span>
<span style="font-size: 0.75rem; color: var(--text-dim);">1980–1985 EASTERN SHOWDOWNS</span>
</div>
<h2 class="card-title">THE GREATEST BATTLES</h2>
<p class="lead-text">
No regular-season games felt like Celtics vs. 76ers. Every meeting was an all-out war for Eastern Conference supremacy.
</p>
<div class="quotes-grid">
<div class="quote-card erving">
<p class="quote-text">"Bird was counting out loud every time he scored: '42 for me, 6 for you, Doc.' That's when things boiled over at Boston Garden."</p>
<div class="quote-author">— The November 9, 1984 Incident (Infamous 42-pt showdown & scuffle)</div>
</div>
<div class="quote-card bird">
<p class="quote-text">"Before Michael Jordan, there was Julius. He carried the whole league on his back. Playing against him made you push beyond whatever limits you thought you had."</p>
<div class="quote-author">— Larry Bird on Julius Erving's legacy</div>
</div>
<div class="quote-card">
<p class="quote-text">"Celtics-Sixers in the early '80s wasn't just basketball. It was high art collided with a street fight. You had to have heart to survive."</p>
<div class="quote-author">— Pat Riley, Lakers Head Coach</div>
</div>
</div>
</div>
</div>
</section>
<!-- TAB 4: TRIVIA CHALLENGE -->
<section id="tab-trivia" class="tab-panel">
<div class="quiz-card">
<span class="retro-pill">SUPERSTAR TRIVIA</span>
<h2 class="card-title" style="margin-top: 0.5rem;" id="quizQuestion">
What iconic feature surprised players in the 1983 "One on One: Dr. J vs. Larry Bird" game?
</h2>
<p style="color: var(--text-dim); font-size: 0.85rem;" id="quizFeedback">
Select an answer below to test your basketball & gaming history knowledge:
</p>
<div class="quiz-options" id="quizOptionsContainer">
<button class="quiz-btn" onclick="checkAnswer(0)">A) Michael Jordan made a surprise guest appearance</button>
<button class="quiz-btn" onclick="checkAnswer(1)">B) Ferocious dunks shattered the glass and a janitor swept it up</button>
<button class="quiz-btn" onclick="checkAnswer(2)">C) The crowd threw digital hot dogs onto the court</button>
<button class="quiz-btn" onclick="checkAnswer(3)">D) You could shoot 5-point shots from the locker room tunnel</button>
</div>
<div style="display: flex; justify-content: center; gap: 12px; margin-top: 1rem;">
<button class="btn-arcade" onclick="loadNextQuestion()">Next Question ➡️</button>
</div>
</div>
</section>
</main>
<footer>
<p>A tribute to the pioneers of basketball art and digital sports games. Built with Vanilla Web Standards & Canvas.</p>
<p style="margin-top: 6px; font-size: 0.75rem; color: #64748b;">
Julius Erving & Larry Bird are members of the Naismith Memorial Basketball Hall of Fame and the NBA 75th Anniversary Team.
</p>
</footer>
<script>
/* ==============================================================
1. SOUND SYNTHESIZER (WEB AUDIO API)
============================================================== */
let audioCtx = null;
let soundEnabled = true;
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
}
function playSound(type) {
if (!soundEnabled) return;
try {
initAudio();
const now = audioCtx.currentTime;
if (type === 'dribble') {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(140, now);
osc.frequency.exponentialRampToValueAtTime(40, now + 0.08);
gain.gain.setValueAtTime(0.35, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.08);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now);
osc.stop(now + 0.09);
} else if (type === 'swish') {
// Swish / Net sound (filtered noise)
const bufferSize = audioCtx.sampleRate * 0.2;
const buffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
const noise = audioCtx.createBufferSource();
noise.buffer = buffer;
const filter = audioCtx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(1200, now);
filter.Q.setValueAtTime(3.0, now);
const gain = audioCtx.createGain();
gain.gain.setValueAtTime(0.4, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.2);
noise.connect(filter);
filter.connect(gain);
gain.connect(audioCtx.destination);
noise.start(now);
} else if (type === 'rim') {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(360, now);
osc.frequency.exponentialRampToValueAtTime(180, now + 0.15);
gain.gain.setValueAtTime(0.5, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.15);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now);
osc.stop(now + 0.16);
} else if (type === 'shatter') {
// Glass shattering sound (multiple rapid white noise & high pitch bursts)
for (let i = 0; i < 5; i++) {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(800 + Math.random() * 1800, now + i * 0.04);
osc.frequency.exponentialRampToValueAtTime(200, now + 0.3 + i * 0.05);
gain.gain.setValueAtTime(0.3, now + i * 0.04);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.35 + i * 0.05);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now + i * 0.04);
osc.stop(now + 0.4 + i * 0.05);
}
} else if (type === 'cheer') {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(523.25, now); // C5
osc.frequency.setValueAtTime(659.25, now + 0.1); // E5
osc.frequency.setValueAtTime(783.99, now + 0.2); // G5
gain.gain.setValueAtTime(0.3, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.4);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now);
osc.stop(now + 0.42);
}
} catch (e) {
console.error("Audio error", e);
}
}
function toggleAudio() {
soundEnabled = !soundEnabled;
const btn = document.getElementById('soundToggleBtn');
btn.textContent = soundEnabled ? '🔊' : '🔇';
if (soundEnabled) playSound('dribble');
}
function toggleCRT() {
document.body.classList.toggle('crt-active');
const btn = document.getElementById('crtToggleBtn');
btn.style.borderColor = document.body.classList.contains('crt-active') ? '#f1c40f' : 'var(--border-glass)';
}
/* ==============================================================
2. TAB NAVIGATION
============================================================== */
function switchTab(tabId) {
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
const target = document.getElementById('tab-' + tabId);
if (target) target.classList.add('active');
const btn = Array.from(document.querySelectorAll('.nav-btn')).find(b => b.getAttribute('onclick').includes(tabId));
if (btn) btn.classList.add('active');
}
/* ==============================================================
3. 1-ON-1 BASKETBALL ARCADE ENGINE
============================================================== */
const canvas = document.getElementById('basketballCourt');
const ctx = canvas.getContext('2d');
let scoreErving = 0;
let scoreBird = 0;
let gameTimeRemaining = 90; // seconds
let shotClock = 24;
let controlledPlayer = 'erving'; // 'erving' or 'bird'
// Court dimensions & positions
const hoop = {
x: 820,
y: 260,
radius: 18,
backboardX: 845,
backboardTop: 200,
backboardBottom: 320,
shattered: false,
glassParticles: []
};
// Players
const players = {
erving: {
id: 'erving',
name: 'Dr. J',
number: '6',
x: 450,
y: 260,
vx: 0,
vy: 0,
speed: 4.8,
color: '#ed174c',
accentColor: '#006bb6',
hairColor: '#1a110b', // Iconic Afro
hairStyle: 'afro',
radius: 20,
hasBall: true,
isDunking: false,
dunkProgress: 0,
shooting: false,
shotCharge: 0
},
bird: {
id: 'bird',
name: 'Bird',
number: '33',
x: 620,
y: 260,
vx: 0,
vy: 0,
speed: 4.3,
color: '#008348',
accentColor: '#ffffff',
hairColor: '#f1c40f', // Blonde mop
hairStyle: 'shag',
radius: 20,
hasBall: false,
isDunking: false,
dunkProgress: 0,
shooting: false,
shotCharge: 0
}
};
// Ball physics
const ball = {
x: players.erving.x + 15,
y: players.erving.y,
z: 0, // height off floor
vx: 0,
vy: 0,
vz: 0,
inFlight: false,
shooter: null,
targetHoop: hoop,
is3Point: false,
dribbleCycle: 0
};
// Keyboard state
const keys = {
ArrowUp: false,
ArrowDown: false,
ArrowLeft: false,
ArrowRight: false,
w: false,
s: false,
a: false,
d: false,
Space: false,
Shift: false
};
window.addEventListener('keydown', (e) => {
initAudio();
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {
e.preventDefault();
}
if (e.key === ' ') keys.Space = true;
if (e.key === 'Shift') keys.Shift = true;
if (e.key.toLowerCase() === 'w' || e.key === 'ArrowUp') keys.w = true;
if (e.key.toLowerCase() === 's' || e.key === 'ArrowDown') keys.s = true;
if (e.key.toLowerCase() === 'a' || e.key === 'ArrowLeft') keys.a = true;
if (e.key.toLowerCase() === 'd' || e.key === 'ArrowRight') keys.d = true;
// Handle shot initiation
handleActionPress();
});
window.addEventListener('keyup', (e) => {
if (e.key === ' ') {
keys.Space = false;
handleShotRelease();
}
if (e.key === 'Shift') keys.Shift = false;
if (e.key.toLowerCase() === 'w' || e.key === 'ArrowUp') keys.w = false;
if (e.key.toLowerCase() === 's' || e.key === 'ArrowDown') keys.s = false;
if (e.key.toLowerCase() === 'a' || e.key === 'ArrowLeft') keys.a = false;
if (e.key.toLowerCase() === 'd' || e.key === 'ArrowRight') keys.d = false;
});
function getActivePlayer() {
return players[controlledPlayer];
}
function getAIPlayer() {
return controlledPlayer === 'erving' ? players.bird : players.erving;
}
function switchPlayerControlled() {
controlledPlayer = controlledPlayer === 'erving' ? 'bird' : 'erving';
document.getElementById('currentControlledLabel').textContent = controlledPlayer === 'erving' ? 'Dr. J (You)' : 'Larry Bird (You)';
showAlertBanner("CONTROL SWITCHED: " + (controlledPlayer === 'erving' ? "DR. J (PHILADELPHIA)" : "LARRY BIRD (CELTICS)"));
}
function handleActionPress() {
const p = getActivePlayer();
if (p.hasBall && !ball.inFlight && keys.Space) {
// Charge shot or prepare dunk
const distToHoop = Math.hypot(hoop.x - p.x, hoop.y - p.y);
if (distToHoop < 160) {
// Close in! Dunk initiation!
startDunk(p);
} else {
p.shooting = true;
p.shotCharge = 0;
}
}
if (!p.hasBall && keys.Shift) {
// Attempt steal
attemptSteal(p, getAIPlayer());
}
}
function handleShotRelease() {
const p = getActivePlayer();
if (p.shooting && p.hasBall && !ball.inFlight) {
executeJumpShot(p, p.shotCharge);
p.shooting = false;
p.shotCharge = 0;
}
}
function attemptSteal(stealer, ballCarrier) {
if (!ballCarrier.hasBall || ball.inFlight) return;
const dist = Math.hypot(stealer.x - ballCarrier.x, stealer.y - ballCarrier.y);
if (dist < 55) {
if (Math.random() < 0.38) {
ballCarrier.hasBall = false;
stealer.hasBall = true;
playSound('dribble');
showAlertBanner(stealer.name.toUpperCase() + " STRIPS THE BALL! STEAL!");
} else {
playSound('rim');
showAlertBanner("REACH-IN CONTEST! BALL PROTECTED");
}
}
}
function startDunk(p) {
p.isDunking = true;
p.dunkProgress = 0;
ball.inFlight = true;
p.hasBall = false;
showAlertBanner(p.id === 'erving' ? "DR. J TAKES FLIGHT! CRADLE SLAM!" : "BIRD POWERS TO THE RIM!");
}
function executeJumpShot(p, charge) {
p.hasBall = false;
ball.inFlight = true;
ball.shooter = p;
ball.x = p.x;
ball.y = p.y;
ball.z = 24;
// Determine if 3 pointer (standard 3-point line roughly x < 560 or arc dist)
const distToHoop = Math.hypot(hoop.x - p.x, hoop.y - p.y);
ball.is3Point = distToHoop > 320;
// Accuracy calculation based on player strengths
let baseChance = 0.55;
if (p.id === 'bird') {
baseChance = ball.is3Point ? 0.72 : 0.65;
} else {
baseChance = ball.is3Point ? 0.35 : 0.68;
}
// Timing bonus: charge sweet spot around 15-25 frames
const timingFactor = Math.max(0, 1 - Math.abs(charge - 20) / 25);
const isGood = Math.random() < (baseChance * 0.7 + timingFactor * 0.3);
// Trajectory
const frames = 38;
const targetX = isGood ? hoop.x + (Math.random() * 4 - 2) : hoop.x + (Math.random() * 30 - 15);
const targetY = isGood ? hoop.y + (Math.random() * 4 - 2) : hoop.y + (Math.random() * 30 - 15);
ball.vx = (targetX - ball.x) / frames;
ball.vy = (targetY - ball.y) / frames;
ball.vz = 9.5; // gravity curve
ball.framesRemaining = frames;
ball.willMake = isGood;
playSound('swish');
}
/* AI Opponent Logic */
function updateAI() {
const ai = getAIPlayer();
const user = getActivePlayer();
if (ai.hasBall && !ball.inFlight) {
// AI Offense
const distToHoop = Math.hypot(hoop.x - ai.x, hoop.y - ai.y);
if (ai.id === 'bird') {
// Bird likes to pull up for jumpers or 3-pointers
if (distToHoop > 220 && distToHoop < 340 && Math.random() < 0.02) {
executeJumpShot(ai, 20);
return;
}
} else {
// Dr. J loves driving to the basket to dunk
if (distToHoop < 150 && Math.random() < 0.05) {
startDunk(ai);
return;
}
}
// Drive towards hoop or open lane
const targetX = hoop.x - 120;
const targetY = hoop.y + Math.sin(Date.now() / 600) * 80;
const angle = Math.atan2(targetY - ai.y, targetX - ai.x);
ai.x += Math.cos(angle) * (ai.speed * 0.7);
ai.y += Math.sin(angle) * (ai.speed * 0.7);
// Shot clock desperation
if (shotClock <= 3 && Math.random() < 0.1) {
executeJumpShot(ai, 20);
}
} else if (!ai.hasBall) {
// AI Defense: stay between user and the hoop
const defendX = user.x + (hoop.x - user.x) * 0.45;
const defendY = user.y + (hoop.y - user.y) * 0.45;
const angle = Math.atan2(defendY - ai.y, defendX - ai.x);
const dist = Math.hypot(defendX - ai.x, defendY - ai.y);
if (dist > 8) {
ai.x += Math.cos(angle) * (ai.speed * 0.85);
ai.y += Math.sin(angle) * (ai.speed * 0.85);
}
// Steal contest
if (Math.hypot(ai.x - user.x, ai.y - user.y) < 45 && Math.random() < 0.008) {
attemptSteal(ai, user);
}
}
}
/* Game State Updates */
function updateGame() {
const user = getActivePlayer();
// User movement
let dx = 0;
let dy = 0;
if (keys.w) dy -= 1;
if (keys.s) dy += 1;
if (keys.a) dx -= 1;
if (keys.d) dx += 1;
if (dx !== 0 && dy !== 0) {
dx *= 0.7071;
dy *= 0.7071;
}
if (!user.isDunking) {
user.x += dx * user.speed;
user.y += dy * user.speed;
}
// Charge jump shot meter
if (user.shooting) {
user.shotCharge++;
}
// Constrain inside court boundaries
[players.erving, players.bird].forEach(p => {
p.x = Math.max(50, Math.min(canvas.width - 70, p.x));
p.y = Math.max(50, Math.min(canvas.height - 50, p.y));
});
// Update AI
updateAI();
// Handle Dunk Animation
[players.erving, players.bird].forEach(p => {
if (p.isDunking) {
p.dunkProgress += 0.045;
const t = p.dunkProgress;
p.x = p.x + (hoop.x - 30 - p.x) * 0.12;
p.y = p.y + (hoop.y - p.y) * 0.12;
ball.x = p.x + 12;
ball.y = p.y;
ball.z = Math.sin(t * Math.PI) * 45;
if (t >= 1) {
// Slam dunk finish!
p.isDunking = false;
ball.inFlight = false;
p.hasBall = false;
const pts = 2;
if (p.id === 'erving') scoreErving += pts; else scoreBird += pts;
updateHUD();
// Dr. J has a special chance to shatter the backboard!
const shouldShatter = p.id === 'erving' && Math.random() < 0.4;
if (shouldShatter || hoop.shatteredTrigger) {
triggerShatteredGlass();
} else {
playSound('rim');
playSound('cheer');
showAlertBanner(p.id === 'erving' ? "💥 THE DOCTOR ROCKS THE RIM! 2 POINTS!" : "💥 BIRD WITH THE VICIOUS SLAM!");
}
// Turnover to other player
resetPossessionAfterScore(p.id === 'erving' ? players.bird : players.erving);
}
}
});
// Handle Jump Shot In Flight
if (ball.inFlight && !players.erving.isDunking && !players.bird.isDunking) {
ball.x += ball.vx;
ball.y += ball.vy;
ball.z += ball.vz;
ball.vz -= 0.48; // gravity
ball.framesRemaining--;
if (ball.framesRemaining <= 0) {
ball.inFlight = false;
if (ball.willMake) {
// Swish!
playSound('swish');
playSound('cheer');
const pts = ball.is3Point ? 3 : 2;
if (ball.shooter.id === 'erving') scoreErving += pts; else scoreBird += pts;
updateHUD();
showAlertBanner(
ball.is3Point
? "🎯 " + ball.shooter.name.toUpperCase() + " DRAINS THE 3-POINTER!"
: "🏀 " + ball.shooter.name.toUpperCase() + " HITS THE JUMPER!"
);
resetPossessionAfterScore(ball.shooter.id === 'erving' ? players.bird : players.erving);
} else {
// Clank!
playSound('rim');
showAlertBanner("CLANK! OFF THE BACK OF THE RIM!");
// Loose ball rebound
ball.z = 0;
ball.vx = (Math.random() - 0.5) * 4;
ball.vy = (Math.random() - 0.5) * 4;
assignRebound();
}
}
}
// Ball following player while dribbling
const ballHolder = players.erving.hasBall ? players.erving : (players.bird.hasBall ? players.bird : null);
if (ballHolder && !ball.inFlight) {
ball.dribbleCycle += 0.2;
const dribbleOffset = Math.sin(ball.dribbleCycle) * 12;
ball.x = ballHolder.x + 18;
ball.y = ballHolder.y + dribbleOffset;
ball.z = Math.abs(Math.sin(ball.dribbleCycle)) * 14;
if (Math.floor(ball.dribbleCycle) % 3 === 0 && Math.random() < 0.15) {
playSound('dribble');
}
}
// Update glass shatter particles
if (hoop.glassParticles.length > 0) {
for (let i = hoop.glassParticles.length - 1; i >= 0; i--) {
const pt = hoop.glassParticles[i];
pt.x += pt.vx;
pt.y += pt.vy;
pt.vy += 0.35; // gravity
pt.alpha -= 0.01;
if (pt.y > canvas.height - 40) {
pt.vy *= -0.3;
pt.y = canvas.height - 40;
}
if (pt.alpha <= 0) {
hoop.glassParticles.splice(i, 1);
}
}
}
}
function assignRebound() {
setTimeout(() => {
if (!players.erving.hasBall && !players.bird.hasBall && !ball.inFlight) {
// Whichever is closer gets rebound
const dJ = Math.hypot(players.erving.x - ball.x, players.erving.y - ball.y);
const dB = Math.hypot(players.bird.x - ball.x, players.bird.y - ball.y);
if (dJ < dB) {
players.erving.hasBall = true;
showAlertBanner("DR. J SNATCHES THE OFFENSIVE BOARD!");
} else {
players.bird.hasBall = true;
showAlertBanner("BIRD CLEARS THE DEFENSIVE REBOUND!");
}
shotClock = 24;
}
}, 500);
}
function resetPossessionAfterScore(newCarrier) {
setTimeout(() => {
players.erving.hasBall = false;
players.bird.hasBall = false;
newCarrier.hasBall = true;
newCarrier.x = 240;
newCarrier.y = 260;
ball.x = newCarrier.x + 15;
ball.y = newCarrier.y;
ball.z = 0;
ball.inFlight = false;
shotClock = 24;
}, 700);
}
function triggerShatteredGlass() {
hoop.shattered = true;
playSound('shatter');
showAlertBanner("💥 CRUNCH! DR. J SHATTERS THE BACKBOARD! GAME STOPPED!");
// Spawn glass shards
for (let i = 0; i < 70; i++) {
hoop.glassParticles.push({
x: hoop.backboardX + (Math.random() * 10 - 5),
y: hoop.backboardTop + Math.random() * (hoop.backboardBottom - hoop.backboardTop),
vx: -(Math.random() * 8 + 2),
vy: (Math.random() - 0.5) * 9,
size: Math.random() * 5 + 2,
alpha: 1.0,
color: Math.random() > 0.4 ? '#e2e8f0' : '#38bdf8'
});
}
// Auto-repair after 4 seconds (like the nostalgic janitor in the 1983 EA game)
setTimeout(() => {
hoop.shattered = false;
showAlertBanner("🧹 JANITOR SWEEPS UP THE SHARDS... NEW BACKBOARD INSTALLED!");
}, 4000);
}
function triggerShatteredGlassTest() {
triggerShatteredGlass();
}
function updateHUD() {
document.getElementById('scoreErving').textContent = scoreErving.toString().padStart(2, '0');
document.getElementById('scoreBird').textContent = scoreBird.toString().padStart(2, '0');
document.getElementById('shotClock').textContent = Math.max(0, shotClock).toString().padStart(2, '0');
}
function showAlertBanner(msg) {
const banner = document.getElementById('gameAlertBanner');
banner.textContent = msg;
banner.classList.add('visible');
clearTimeout(banner.timer);
banner.timer = setTimeout(() => {
banner.classList.remove('visible');
}, 2400);
}
function resetGameMatch() {
scoreErving = 0;
scoreBird = 0;
gameTimeRemaining = 90;
shotClock = 24;
players.erving.x = 420;
players.erving.y = 260;
players.erving.hasBall = true;
players.bird.x = 640;
players.bird.y = 260;
players.bird.hasBall = false;
ball.inFlight = false;
updateHUD();
showAlertBanner("MATCH RESTARTED! TIP OFF!");
}
// Game loop timers (clock countdown)
setInterval(() => {
if (gameTimeRemaining > 0) {
gameTimeRemaining--;
const mins = Math.floor(gameTimeRemaining / 60);
const secs = gameTimeRemaining % 60;
document.getElementById('gameTimer').textContent =
mins.toString().padStart(2, '0') + ":" + secs.toString().padStart(2, '0');
if (shotClock > 0) {
shotClock--;
} else {
// Shot clock violation
shotClock = 24;
showAlertBanner("⏰ 24-SECOND SHOT CLOCK VIOLATION! TURNOVER!");
playSound('rim');
if (players.erving.hasBall) {
players.erving.hasBall = false;
players.bird.hasBall = true;
} else {
players.bird.hasBall = false;
players.erving.hasBall = true;
}
}
updateHUD();
} else {
showAlertBanner("FINAL BUZZER! GAME OVER!");
}
}, 1000);
/* ==============================================================
4. COURT & PLAYER RENDERING (CANVAS)
============================================================== */
function drawCourt() {
// Hardwood floor gradient
const grad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
grad.addColorStop(0, '#c68a4c');
grad.addColorStop(0.5, '#b97b3d');
grad.addColorStop(1, '#a66a2f');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Wood plank seams
ctx.strokeStyle = 'rgba(0, 0, 0, 0.05)';
ctx.lineWidth = 1;
for (let y = 0; y < canvas.height; y += 22) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
// Court Lines (White / Cream)
ctx.strokeStyle = 'rgba(255, 255, 255, 0.85)';
ctx.lineWidth = 3;
// Outer boundary line
ctx.strokeRect(30, 30, canvas.width - 60, canvas.height - 60);
// Half-court division line
ctx.beginPath();
ctx.moveTo(120, 30);
ctx.lineTo(120, canvas.height - 30);
ctx.stroke();
// Center Circle
ctx.beginPath();
ctx.arc(120, canvas.height / 2, 60, -Math.PI / 2, Math.PI / 2);
ctx.stroke();
// The Paint / Key (Boston Green & Sixers Blue Tint)
ctx.fillStyle = 'rgba(0, 107, 182, 0.25)';
ctx.fillRect(canvas.width - 240, 170, 180, 180);
ctx.strokeRect(canvas.width - 240, 170, 180, 180);
// Free throw circle
ctx.beginPath();
ctx.arc(canvas.width - 240, canvas.height / 2, 55, Math.PI / 2, (3 * Math.PI) / 2);
ctx.stroke();
// 3-Point Arc
ctx.beginPath();
ctx.arc(hoop.x, hoop.y, 250, 0.65 * Math.PI, 1.35 * Math.PI);
ctx.stroke();
// Backboard & Hoop
ctx.save();
if (hoop.shattered) {
// Jagged broken backboard frame
ctx.strokeStyle = '#94a3b8';
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(hoop.backboardX, hoop.backboardTop);
ctx.lineTo(hoop.backboardX - 4, hoop.backboardTop + 30);
ctx.lineTo(hoop.backboardX + 2, hoop.backboardBottom - 20);
ctx.stroke();
} else {
// Glass Backboard
ctx.fillStyle = 'rgba(226, 232, 240, 0.8)';
ctx.fillRect(hoop.backboardX, hoop.backboardTop, 8, hoop.backboardBottom - hoop.backboardTop);
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2;
ctx.strokeRect(hoop.backboardX, hoop.backboardTop, 8, hoop.backboardBottom - hoop.backboardTop);
// Inner square on backboard
ctx.strokeRect(hoop.backboardX - 1, hoop.y - 25, 4, 50);
}
// Rim & Net
ctx.strokeStyle = '#ff4500'; // Orange rim
ctx.lineWidth = 4;
ctx.beginPath();
ctx.arc(hoop.x, hoop.y, hoop.radius, 0, Math.PI * 2);
ctx.stroke();
// Net cords
ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';
ctx.lineWidth = 1.5;
for (let i = -12; i <= 12; i += 6) {
ctx.beginPath();
ctx.moveTo(hoop.x + i, hoop.y);
ctx.lineTo(hoop.x + i * 0.5, hoop.y + 16);
ctx.stroke();
}
ctx.restore();
// Draw Glass Shards if shattered
hoop.glassParticles.forEach(pt => {
ctx.fillStyle = pt.color;
ctx.globalAlpha = pt.alpha;
ctx.fillRect(pt.x, pt.y, pt.size, pt.size);
});
ctx.globalAlpha = 1.0;
}
function drawPlayer(p) {
ctx.save();
// Player drop shadow on court floor
ctx.fillStyle = 'rgba(0, 0, 0, 0.35)';
ctx.beginPath();
ctx.ellipse(p.x, p.y + p.radius + 4, p.radius * 0.9, p.radius * 0.45, 0, 0, Math.PI * 2);
ctx.fill();
// Controlled player indicator ring
if (p.id === controlledPlayer) {
ctx.strokeStyle = '#f1c40f';
ctx.lineWidth = 2;
ctx.setLineDash([4, 4]);
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius + 10, 0, Math.PI * 2);
ctx.stroke();
ctx.setLineDash([]);
}
// Player Body / Jersey
ctx.fillStyle = p.color;
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
ctx.fill();
ctx.lineWidth = 3;
ctx.strokeStyle = p.accentColor;
ctx.stroke();
// Player Number on Back/Chest
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 12px Bebas Neue, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(p.number, p.x, p.y + 1);
// Head & Signature Hairstyle
// Head base
const headRadius = 9;
const headY = p.y - p.radius - 2;
ctx.fillStyle = p.id === 'erving' ? '#6b4423' : '#fcd34d'; // Skin tones
ctx.beginPath();
ctx.arc(p.x, headY, headRadius, 0, Math.PI * 2);
ctx.fill();
// Distinct Hairstyles:
if (p.hairStyle === 'afro') {
// Dr. J's legendary massive round Afro
ctx.fillStyle = '#110c08';
ctx.beginPath();
ctx.arc(p.x, headY - 4, headRadius + 5, 0, Math.PI * 2);
ctx.fill();
// Forehead headband
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.arc(p.x, headY, headRadius, 0.8 * Math.PI, 0.2 * Math.PI);
ctx.stroke();
} else {
// Larry Bird's blonde fringe / mop cut
ctx.fillStyle = '#eab308';
ctx.beginPath();
ctx.arc(p.x, headY - 3, headRadius + 2, 0.9 * Math.PI, 0.1 * Math.PI);
ctx.fill();
}
// Shot Power Meter Gauge
if (p.shooting) {
const meterW = 36;
const meterH = 6;
const fill = Math.min(1, p.shotCharge / 30);
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
ctx.fillRect(p.x - meterW / 2, p.y - 45, meterW, meterH);
ctx.fillStyle = fill > 0.6 && fill < 0.85 ? '#22c55e' : '#ef4444';
ctx.fillRect(p.x - meterW / 2, p.y - 45, meterW * fill, meterH);
ctx.strokeStyle = '#fff';
ctx.lineWidth = 1;
ctx.strokeRect(p.x - meterW / 2, p.y - 45, meterW, meterH);
}
ctx.restore();
}
function drawBall() {
ctx.save();
const ballRadius = 9;
// Ball shadow on court
ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
const shadowSize = Math.max(2, ballRadius - ball.z * 0.15);
ctx.beginPath();
ctx.ellipse(ball.x, ball.y + ball.z + 6, shadowSize, shadowSize * 0.5, 0, 0, Math.PI * 2);
ctx.fill();
// Ball body
const drawY = ball.y - ball.z;
ctx.fillStyle = '#ea580c'; // Basketball leather orange
ctx.beginPath();
ctx.arc(ball.x, drawY, ballRadius, 0, Math.PI * 2);
ctx.fill();
// Black Ribs / Seams
ctx.strokeStyle = '#18181b';
ctx.lineWidth = 1.4;
ctx.beginPath();
ctx.arc(ball.x, drawY, ballRadius, -Math.PI / 4, Math.PI / 4);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(ball.x - ballRadius, drawY);
ctx.lineTo(ball.x + ballRadius, drawY);
ctx.stroke();
ctx.restore();
}
function gameRenderLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawCourt();
updateGame();
drawPlayer(players.bird);
drawPlayer(players.erving);
drawBall();
requestAnimationFrame(gameRenderLoop);
}
// Start Arcade Loop
requestAnimationFrame(gameRenderLoop);
/* ==============================================================
5. TRIVIA ENGINE
============================================================== */
const triviaQuestions = [
{
q: "What iconic feature shocked players in the 1983 'One on One: Dr. J vs. Larry Bird' game?",
options: [
"A) Michael Jordan made a secret cameo",
"B) Monster dunks shattered the glass and a janitor came to sweep it",
"C) The mascot shot t-shirts into the crowd",
"D) You could shoot 5-pointers from the bench"
],
correct: 1,
fact: "EA founder Trip Hawkins and programmer Eric Hammond included the shattered backboard and grumbling janitor as an Easter egg homage to Darryl Dawkins!"
},
{
q: "In their regular season head-to-head career meetings, what was the exact record between Dr. J and Larry Bird?",
options: [
"A) Erving led 24-12",
"B) Bird led 28-8",
"C) Exactly dead even at 18 wins each (18-18)",
"D) Erving won all 36 games"
],
correct: 2,
fact: "Across 36 regular season games, Julius Erving and Larry Bird split their games exactly 18-18, making it one of the most balanced superstar duels in sports history!"
},
{
q: "During the famous November 9, 1984 clash at Boston Garden, what caused the tensions to explode between Erving and Bird?",
options: [
"A) A spilled water cooler on the Sixers bench",
"B) Bird taunting Dr. J by chanting his 42 points vs Erving's 6 points",
"C) A dispute over who had the better signature shoe",
"D) The referee accidentally called a timeout for the wrong team"
],
correct: 1,
fact: "Bird had 42 points while Erving was having an off night with 6. Bird repeatedly muttered '42 to 6, Doc' until tempers flared into an iconic benches-clearing scuffle!"
},
{
q: "Which company published the original 'One on One: Dr. J vs. Larry Bird' in 1983?",
options: [
"A) Nintendo",
"B) Atari",
"C) Electronic Arts (EA)",
"D) Sega"
],
correct: 2,
fact: "It was one of EA's earliest landmark commercial hits, cementing their reputation before the EA Sports brand was even formalized!"
}
];
let currentQIdx = 0;
function renderQuestion() {
const q = triviaQuestions[currentQIdx];
document.getElementById('quizQuestion').textContent = q.q;
document.getElementById('quizFeedback').textContent = "Select an answer below to test your basketball knowledge:";
const container = document.getElementById('quizOptionsContainer');
container.innerHTML = "";
q.options.forEach((opt, idx) => {
const btn = document.createElement('button');
btn.className = 'quiz-btn';
btn.textContent = opt;
btn.onclick = () => checkAnswer(idx);
container.appendChild(btn);
});
}
function checkAnswer(chosenIdx) {
const q = triviaQuestions[currentQIdx];
const buttons = document.querySelectorAll('.quiz-btn');
buttons.forEach((btn, idx) => {
btn.disabled = true;
if (idx === q.correct) {
btn.classList.add('correct');
} else if (idx === chosenIdx) {
btn.classList.add('wrong');
}
});
if (chosenIdx === q.correct) {
playSound('cheer');
document.getElementById('quizFeedback').textContent = "🎉 CORRECT! " + q.fact;
} else {
playSound('rim');
document.getElementById('quizFeedback').textContent = "❌ NOT QUITE! " + q.fact;
}
}
function loadNextQuestion() {
currentQIdx = (currentQIdx + 1) % triviaQuestions.length;
renderQuestion();
}
</script>
</body>
</html>