Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"lucide-react": "^0.344.0",
"nodemailer": "^7.0.5",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"react-router-dom": "^6.30.3"
},
"devDependencies": {
"@eslint/js": "^9.9.1",
Expand Down
12 changes: 11 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { Navigate, Route, Routes } from 'react-router-dom';
import LabExperimentPage from './lab/pages/LabExperimentPage';
import LabIndexPage from './lab/pages/LabIndexPage';
import HomePage from './pages/HomePage';

function App() {
return <HomePage />;
return (
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/lab" element={<LabIndexPage />} />
<Route path="/lab/:slug" element={<LabExperimentPage />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
);
}

export default App;
12 changes: 6 additions & 6 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
const navItems = [
{ label: 'Work', href: '#work' },
{ label: 'XR Performance Lab', href: '#xr-performance-lab' },
{ label: 'Writing', href: '#writing' },
{ label: 'About', href: '#about' },
{ label: 'Contact', href: '#contact' }
{ label: 'Work', href: '/#work' },
{ label: 'XR Performance Lab', href: '/lab' },
{ label: 'Writing', href: '/#writing' },
{ label: 'About', href: '/#about' },
{ label: 'Contact', href: '/#contact' }
];

export default function Navbar() {
return (
<header className="sticky top-0 z-50 border-b border-slate-200/80 bg-white/90 backdrop-blur">
<nav className="mx-auto flex w-full max-w-6xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
<a href="#work" className="text-sm font-semibold tracking-tight text-slate-900">
<a href="/#work" className="text-sm font-semibold tracking-tight text-slate-900">
Real-Time Performance
</a>
<div className="hidden items-center gap-6 md:flex">
Expand Down
30 changes: 30 additions & 0 deletions src/lab/components/EvidenceGallery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { EvidenceItem } from '../types';

type EvidenceGalleryProps = {
evidence: EvidenceItem[];
title?: string;
};

export default function EvidenceGallery({ evidence, title = 'Evidence' }: EvidenceGalleryProps) {
return (
<section className="space-y-5">
<h2 className="text-xl font-semibold text-slate-900">{title}</h2>
<div className="space-y-8">
{evidence.map((item) => (
<article key={item.title} className="space-y-2">
<h3 className="text-base font-semibold text-slate-900">{item.title}</h3>
{item.description && <p className="text-sm text-slate-600">{item.description}</p>}
<img
src={item.imagePath}
alt={item.title}
loading="lazy"
decoding="async"
className="w-full rounded-lg border border-slate-200"
/>
<p className="text-xs text-slate-500/90">{item.caption}</p>
</article>
))}
</div>
</section>
);
}
14 changes: 14 additions & 0 deletions src/lab/components/ExperimentHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { LabExperiment } from '../types';

type ExperimentHeaderProps = Pick<LabExperiment, 'title' | 'subtitle' | 'lastUpdated' | 'context'>;

export default function ExperimentHeader({ title, subtitle, lastUpdated, context }: ExperimentHeaderProps) {
return (
<header className="space-y-3 border-b border-slate-200 pb-6">
<h1 className="text-3xl font-semibold tracking-tight text-slate-900 sm:text-4xl">{title}</h1>
<p className="text-base text-slate-700">{subtitle}</p>
{lastUpdated && <p className="text-xs text-slate-500">Last updated: {lastUpdated}</p>}
{context && <p className="text-sm text-slate-600">{context}</p>}
</header>
);
}
11 changes: 11 additions & 0 deletions src/lab/components/ExperimentNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Link } from 'react-router-dom';

export default function ExperimentNav() {
return (
<div>
<Link to="/lab" className="text-sm text-slate-600 underline-offset-2 hover:text-cyan-700 hover:underline">
← Back to Lab
</Link>
</div>
);
}
31 changes: 31 additions & 0 deletions src/lab/components/MetricsSummary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { LabMetricsSummary } from '../types';

type MetricsSummaryProps = {
metrics: LabMetricsSummary;
};

export default function MetricsSummary({ metrics }: MetricsSummaryProps) {
return (
<section className="space-y-4">
<div className="flex flex-wrap items-center gap-3">
<h2 className="text-xl font-semibold text-slate-900">Metrics Summary</h2>
{metrics.headline && <span className="rounded-full border border-slate-300 px-3 py-1 text-xs text-slate-700">{metrics.headline}</span>}
</div>

<div className="grid grid-cols-1 gap-3 sm:grid-cols-1 lg:grid-cols-3">
{metrics.rows.map((row) => (
<div key={row.label} className="rounded-xl border border-slate-200 bg-white p-4">
<p className="text-xs uppercase tracking-wide text-slate-500">{row.label}</p>
<p className="mt-2 text-xl font-semibold text-slate-900">{row.value}</p>
{row.hint && <p className="mt-1 text-xs text-slate-600">{row.hint}</p>}
</div>
))}
</div>

<p className="text-sm text-slate-700">
<span className="font-semibold text-slate-900">Bottleneck:</span> {metrics.bottleneck}
{metrics.bottleneckDetail ? ` — ${metrics.bottleneckDetail}` : ''}
</p>
</section>
);
}
16 changes: 16 additions & 0 deletions src/lab/components/TechnicalTakeaways.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type TechnicalTakeawaysProps = {
takeaways: string[];
};

export default function TechnicalTakeaways({ takeaways }: TechnicalTakeawaysProps) {
return (
<section className="space-y-3">
<h2 className="text-xl font-semibold text-slate-900">Technical Takeaways</h2>
<ul className="list-disc space-y-2 pl-5 text-sm text-slate-700 marker:text-slate-500">
{takeaways.slice(0, 5).map((takeaway) => (
<li key={takeaway}>{takeaway}</li>
))}
</ul>
</section>
);
}
50 changes: 50 additions & 0 deletions src/lab/data/experiments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { LabExperiment } from '../types';

export const labExperiments: LabExperiment[] = [
{
slug: 'overdraw',
title: 'Overdraw Stress Test',
subtitle: 'Transparent Stacking',
lastUpdated: '2026-02-25',
context: 'Controlled transparency stacking to quantify overdraw cost under XR-style fragment pressure.',
metrics: {
headline: 'RenderLoop (GPU)',
rows: [
{ label: 'Baseline Render', value: '6.37 ms' },
{ label: 'Overdraw Render', value: '13.64 ms' },
{ label: 'Delta', value: '+7.27 ms', hint: 'CPU stable ~0.5 ms' }
],
bottleneck: 'GPU-bound',
bottleneckDetail: 'Fragment / overdraw (alpha blending)'
},
evidence: [
{
title: 'Unity Profiler Comparison',
description: 'Baseline and stress comparison under identical scene conditions.',
imagePath: '/lab/Overdraw_CPU_RenderThread_Comparison.png',
caption: 'Baseline render at 6.37 ms versus overdraw render at 13.64 ms with CPU stable near 0.5 ms.'
},
{
title: 'Frame Debugger Transparent Pass',
description: 'Transparent pass breakdown during stacked alpha rendering.',
imagePath: '/lab/Overdraw_FrameDebugger_TransparentPass.png',
caption: 'Transparent pass cost rises sharply as layered alpha surfaces multiply fragment processing.'
},
{
title: 'Experiment Summary Capture',
description: 'Consolidated capture of profiler and debugging evidence.',
imagePath: '/lab/Overdraw_Experiment_Summary.png',
caption: 'Summary view confirms the fragment bottleneck signature under controlled overdraw stress.'
}
],
takeaways: [
'Transparency reduces early-Z effectiveness; stacked layers multiply fragment work.',
'XR stereo amplifies overdraw cost (per-eye fragment workload).',
'SrcAlpha/OneMinusSrcAlpha blending behaves as a fill-rate multiplier under stacking.',
'Mitigation: reduce transparent layers, convert to opaque/cutout, constrain stacking.'
]
}
];

export const getExperimentBySlug = (slug: string): LabExperiment | undefined =>
labExperiments.find((experiment) => experiment.slug === slug);
35 changes: 35 additions & 0 deletions src/lab/pages/LabExperimentPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Navigate, useParams } from 'react-router-dom';
import Navbar from '../../components/Navbar';
import EvidenceGallery from '../components/EvidenceGallery';
import ExperimentHeader from '../components/ExperimentHeader';
import ExperimentNav from '../components/ExperimentNav';
import MetricsSummary from '../components/MetricsSummary';
import TechnicalTakeaways from '../components/TechnicalTakeaways';
import { getExperimentBySlug } from '../data/experiments';

export default function LabExperimentPage() {
const { slug } = useParams<{ slug: string }>();
const experiment = slug ? getExperimentBySlug(slug) : undefined;

if (!experiment) {
return <Navigate to="/lab" replace />;
}

return (
<div className="min-h-screen bg-site-pattern text-slate-900">
<Navbar />
<main className="mx-auto flex max-w-5xl flex-col gap-10 px-4 py-12 sm:px-6 lg:px-8">
<ExperimentNav />
<ExperimentHeader
title={experiment.title}
subtitle={experiment.subtitle}
lastUpdated={experiment.lastUpdated}
context={experiment.context}
/>
<MetricsSummary metrics={experiment.metrics} />
<EvidenceGallery evidence={experiment.evidence} title="Evidence (Profiler + Captures)" />
<TechnicalTakeaways takeaways={experiment.takeaways} />
</main>
</div>
);
}
51 changes: 51 additions & 0 deletions src/lab/pages/LabIndexPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Link } from 'react-router-dom';
import Navbar from '../../components/Navbar';
import ResultsTable from '../../components/ResultsTable';
import { performanceResults } from '../../data/performanceResults';
import { labExperiments } from '../data/experiments';

export default function LabIndexPage() {
return (
<div className="min-h-screen bg-site-pattern text-slate-900">
<Navbar />
<main className="mx-auto max-w-6xl space-y-10 px-4 py-12 sm:px-6 lg:px-8">
<section className="space-y-3">
<h1 className="text-3xl font-semibold tracking-tight text-slate-900 sm:text-4xl">XR Performance Lab</h1>
<p className="text-sm text-slate-700">
Controlled experiment index for profiling bottlenecks in XR rendering pipelines.
</p>
</section>

<section className="space-y-4">
<h2 className="text-xl font-semibold text-slate-900">Experiments</h2>
<div className="space-y-3">
{labExperiments.map((experiment) => (
<Link
key={experiment.slug}
to={`/lab/${experiment.slug}`}
className="block rounded-xl border border-slate-200 bg-white p-4 transition hover:border-cyan-700"
>
<p className="text-lg font-semibold text-slate-900">
{experiment.title} <span className="font-normal text-slate-700">({experiment.subtitle})</span>
</p>
<p className="mt-1 text-sm text-slate-700">
Bottleneck: {experiment.metrics.bottleneck}
{experiment.metrics.bottleneckDetail ? ` — ${experiment.metrics.bottleneckDetail}` : ''}
</p>
{experiment.lastUpdated && <p className="mt-2 text-xs text-slate-500">Last updated: {experiment.lastUpdated}</p>}
</Link>
))}
</div>
</section>

<section className="space-y-4">
<div>
<h2 className="text-xl font-semibold text-slate-900">Published Results</h2>
<p className="text-xs text-slate-500">Aggregated benchmark table for all tracked runs.</p>
</div>
<ResultsTable rows={performanceResults} />
</section>
</main>
</div>
);
}
32 changes: 32 additions & 0 deletions src/lab/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export type BottleneckType = 'GPU-bound' | 'CPU-bound' | 'Balanced' | 'Unknown';

export type LabMetricRow = {
label: string;
value: string;
hint?: string;
};

export type LabMetricsSummary = {
headline?: string;
rows: LabMetricRow[];
bottleneck: BottleneckType;
bottleneckDetail?: string;
};

export type EvidenceItem = {
title: string;
description?: string;
imagePath: string;
caption: string;
};

export type LabExperiment = {
slug: string;
title: string;
subtitle: string;
lastUpdated?: string;
context?: string;
metrics: LabMetricsSummary;
evidence: EvidenceItem[];
takeaways: string[];
};
Loading