From 3859a40525ed96e85d7982af5fa3db85a0e8994c Mon Sep 17 00:00:00 2001 From: James De Raja Date: Tue, 28 Apr 2026 21:18:21 +0530 Subject: [PATCH 1/2] Update canonical site domain to james.alphaden.club --- index.html | 4 +- public/robots.txt | 2 +- public/sitemap.xml | 34 ++-- src/App.tsx | 2 + .../pages/ProfilingToolkitCaseStudyPage.tsx | 2 +- .../PublishedMobileProjectsCaseStudyPage.tsx | 2 +- .../pages/SoftMaskProCaseStudyPage.tsx | 2 +- .../pages/UpdateStrategiesCaseStudyPage.tsx | 2 +- .../pages/UpdateStrategiesVariantAPage.tsx | 2 +- .../pages/UpdateStrategiesVariantBPage.tsx | 2 +- .../pages/UpdateStrategiesVariantCPage.tsx | 2 +- .../pages/UpdateStrategiesVariantDPage.tsx | 2 +- .../pages/XRStressLabCaseStudyPage.tsx | 2 +- src/components/Footer.tsx | 1 + src/components/Seo.tsx | 2 +- src/components/SmartLink.tsx | 2 +- .../visual-journey/FloatingMetric.tsx | 29 ++++ .../visual-journey/JourneyStage.tsx | 80 +++++++++ .../visual-journey/ScrollProgressRail.tsx | 34 ++++ .../visual-journey/ThoughtBubble.tsx | 29 ++++ .../visual-journey/VisualJourneyCharacter.tsx | 119 +++++++++++++ .../visual-journey/VisualJourneyHero.tsx | 44 +++++ .../visual-journey/VisualJourneyPage.tsx | 159 ++++++++++++++++++ src/components/visual-journey/types.ts | 19 +++ src/lab/pages/FramePacingLabPage.tsx | 2 +- src/lab/pages/FramePacingVsFPSLabPage.tsx | 2 +- src/lab/pages/InstancingLabPage.tsx | 2 +- src/lab/pages/MSAALabPage.tsx | 2 +- src/lab/pages/MSAAOverdrawLabPage.tsx | 2 +- src/lab/pages/OverdrawLabPage.tsx | 2 +- src/lab/pages/OverdrawStereoLabPage.tsx | 2 +- src/lab/pages/XRFrameTimingLabPage.tsx | 2 +- src/pages/HomePage.tsx | 2 +- src/pages/NotFoundPage.tsx | 2 +- src/pages/VisualJourney.tsx | 21 +++ 35 files changed, 581 insertions(+), 38 deletions(-) create mode 100644 src/components/visual-journey/FloatingMetric.tsx create mode 100644 src/components/visual-journey/JourneyStage.tsx create mode 100644 src/components/visual-journey/ScrollProgressRail.tsx create mode 100644 src/components/visual-journey/ThoughtBubble.tsx create mode 100644 src/components/visual-journey/VisualJourneyCharacter.tsx create mode 100644 src/components/visual-journey/VisualJourneyHero.tsx create mode 100644 src/components/visual-journey/VisualJourneyPage.tsx create mode 100644 src/components/visual-journey/types.ts create mode 100644 src/pages/VisualJourney.tsx diff --git a/index.html b/index.html index 1fac162..9a38d28 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,7 @@ name="description" content="Real-time performance engineer with 13+ years in Unity. Shipped mobile titles, XR/VR optimization, GPU/CPU bottleneck isolation, and frame pacing research with profiler-validated measurements." /> - + - + diff --git a/public/robots.txt b/public/robots.txt index fb4fbea..86deee0 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,3 +1,3 @@ User-agent: * Allow: / -Sitemap: https://jamesderaja.com/sitemap.xml +Sitemap: https://james.alphaden.club/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml index 5a9ca7d..1140435 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -1,87 +1,93 @@ - https://jamesderaja.com/ + https://james.alphaden.club/ 2026-03-06 monthly 1.0 - https://jamesderaja.com/case-studies/xr-stress-lab + https://james.alphaden.club/case-studies/xr-stress-lab 2025-01-01 monthly 0.8 - https://jamesderaja.com/case-studies/softmaskpro + https://james.alphaden.club/case-studies/softmaskpro 2025-01-01 monthly 0.8 - https://jamesderaja.com/case-studies/profiling-toolkit + https://james.alphaden.club/case-studies/profiling-toolkit 2025-01-01 monthly 0.8 - https://jamesderaja.com/case-studies/sneaky-warrior-3d + https://james.alphaden.club/case-studies/sneaky-warrior-3d 2025-01-01 monthly 0.7 - https://jamesderaja.com/case-studies/published-mobile-projects + https://james.alphaden.club/case-studies/published-mobile-projects 2025-01-01 monthly 0.7 - https://jamesderaja.com/lab/overdraw + https://james.alphaden.club/lab/overdraw 2025-01-01 monthly 0.7 - https://jamesderaja.com/lab/msaa + https://james.alphaden.club/lab/msaa 2025-01-01 monthly 0.7 - https://jamesderaja.com/lab/msaa-overdraw + https://james.alphaden.club/lab/msaa-overdraw 2025-01-01 monthly 0.7 - https://jamesderaja.com/lab/instancing + https://james.alphaden.club/lab/instancing 2025-01-01 monthly 0.7 - https://jamesderaja.com/lab/frame-pacing + https://james.alphaden.club/lab/frame-pacing 2025-01-01 monthly 0.7 - https://jamesderaja.com/lab/frame-pacing-vs-fps + https://james.alphaden.club/lab/frame-pacing-vs-fps 2025-01-01 monthly 0.7 - https://jamesderaja.com/lab/xr-frame-timing + https://james.alphaden.club/lab/xr-frame-timing 2025-01-01 monthly 0.7 - https://jamesderaja.com/lab/overdraw-stereo + https://james.alphaden.club/lab/overdraw-stereo 2025-01-01 monthly 0.7 + + https://james.alphaden.club/visual-journey + 2026-04-28 + monthly + 0.8 + diff --git a/src/App.tsx b/src/App.tsx index 75d4be0..811d9d3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,6 +19,7 @@ import XRFrameTimingLabPage from './lab/pages/XRFrameTimingLabPage'; import SiteLayout from './layouts/SiteLayout'; import HomePage from './pages/HomePage'; import NotFoundPage from './pages/NotFoundPage'; +import VisualJourney from './pages/VisualJourney'; function App() { return ( @@ -43,6 +44,7 @@ function App() { } /> } /> } /> + } /> } /> diff --git a/src/caseStudies/pages/ProfilingToolkitCaseStudyPage.tsx b/src/caseStudies/pages/ProfilingToolkitCaseStudyPage.tsx index 7ed1558..3bf6294 100644 --- a/src/caseStudies/pages/ProfilingToolkitCaseStudyPage.tsx +++ b/src/caseStudies/pages/ProfilingToolkitCaseStudyPage.tsx @@ -16,7 +16,7 @@ export default function ProfilingToolkitCaseStudyPage() { diff --git a/src/caseStudies/pages/UpdateStrategiesVariantBPage.tsx b/src/caseStudies/pages/UpdateStrategiesVariantBPage.tsx index 2bdbe33..99b3044 100644 --- a/src/caseStudies/pages/UpdateStrategiesVariantBPage.tsx +++ b/src/caseStudies/pages/UpdateStrategiesVariantBPage.tsx @@ -74,7 +74,7 @@ export default function UpdateStrategiesVariantBPage() { diff --git a/src/caseStudies/pages/UpdateStrategiesVariantCPage.tsx b/src/caseStudies/pages/UpdateStrategiesVariantCPage.tsx index b100365..ebbca61 100644 --- a/src/caseStudies/pages/UpdateStrategiesVariantCPage.tsx +++ b/src/caseStudies/pages/UpdateStrategiesVariantCPage.tsx @@ -83,7 +83,7 @@ export default function UpdateStrategiesVariantCPage() { diff --git a/src/caseStudies/pages/UpdateStrategiesVariantDPage.tsx b/src/caseStudies/pages/UpdateStrategiesVariantDPage.tsx index 6b8586f..36fae21 100644 --- a/src/caseStudies/pages/UpdateStrategiesVariantDPage.tsx +++ b/src/caseStudies/pages/UpdateStrategiesVariantDPage.tsx @@ -107,7 +107,7 @@ export default function UpdateStrategiesVariantDPage() { diff --git a/src/caseStudies/pages/XRStressLabCaseStudyPage.tsx b/src/caseStudies/pages/XRStressLabCaseStudyPage.tsx index f9d0c03..1e4d184 100644 --- a/src/caseStudies/pages/XRStressLabCaseStudyPage.tsx +++ b/src/caseStudies/pages/XRStressLabCaseStudyPage.tsx @@ -62,7 +62,7 @@ export default function XRStressLabCaseStudyPage() { LinkedIn GitHub Resume + Visual Journey diff --git a/src/components/Seo.tsx b/src/components/Seo.tsx index 6386987..6784881 100644 --- a/src/components/Seo.tsx +++ b/src/components/Seo.tsx @@ -26,7 +26,7 @@ export function Seo({ '@type': 'Person', name: 'James De Raja', jobTitle: 'Senior Real-Time Performance Engineer', - url: 'https://jamesderaja.com', + url: 'https://james.alphaden.club', email: 'jamesderaja@gmail.com', address: { '@type': 'PostalAddress', diff --git a/src/components/SmartLink.tsx b/src/components/SmartLink.tsx index 6a0c9fb..72f8415 100644 --- a/src/components/SmartLink.tsx +++ b/src/components/SmartLink.tsx @@ -6,7 +6,7 @@ type SmartLinkProps = AnchorHTMLAttributes & { children: ReactNode; }; -const INTERNAL_HOSTNAMES = new Set(['jamesderaja.com', 'www.jamesderaja.com']); +const INTERNAL_HOSTNAMES = new Set(['james.alphaden.club', 'www.james.alphaden.club']); function isHttpUrl(href: string) { return href.startsWith('http://') || href.startsWith('https://'); diff --git a/src/components/visual-journey/FloatingMetric.tsx b/src/components/visual-journey/FloatingMetric.tsx new file mode 100644 index 0000000..0d4608e --- /dev/null +++ b/src/components/visual-journey/FloatingMetric.tsx @@ -0,0 +1,29 @@ +import { motion, useReducedMotion } from 'framer-motion'; + +type FloatingMetricProps = { + text: string; + index?: number; +}; + +export default function FloatingMetric({ text, index = 0 }: FloatingMetricProps) { + const reduceMotion = useReducedMotion(); + + return ( + + {text} + + ); +} diff --git a/src/components/visual-journey/JourneyStage.tsx b/src/components/visual-journey/JourneyStage.tsx new file mode 100644 index 0000000..018acb9 --- /dev/null +++ b/src/components/visual-journey/JourneyStage.tsx @@ -0,0 +1,80 @@ +import { motion, useReducedMotion } from 'framer-motion'; +import FloatingMetric from './FloatingMetric'; +import ThoughtBubble from './ThoughtBubble'; +import VisualJourneyCharacter from './VisualJourneyCharacter'; +import type { JourneyStageData } from './types'; + +type JourneyStageProps = { + stage: JourneyStageData; + index: number; + isActive: boolean; + isMobile: boolean; + onEnter: (index: number) => void; + progress: number; +}; + +export default function JourneyStage({ + stage, + index, + isActive, + isMobile, + onEnter, + progress, +}: JourneyStageProps) { + const reduceMotion = useReducedMotion(); + + return ( + onEnter(index)} + > +
+ + Phase {index + 1} + +

{stage.title}

+
+

{stage.description}

+ + {isMobile && ( +
+ +
+ )} + + {!!stage.chips?.length && ( +
+ {stage.chips.map((chip) => ( + + {chip} + + ))} +
+ )} + +
+ {stage.bubbles.map((bubble, bubbleIndex) => ( + + ))} +
+ + {!!stage.metrics?.length && ( +
+ {stage.metrics.map((metric, metricIndex) => ( + + ))} +
+ )} + + {isActive &&
} + + ); +} diff --git a/src/components/visual-journey/ScrollProgressRail.tsx b/src/components/visual-journey/ScrollProgressRail.tsx new file mode 100644 index 0000000..41f4c2a --- /dev/null +++ b/src/components/visual-journey/ScrollProgressRail.tsx @@ -0,0 +1,34 @@ +import { motion } from 'framer-motion'; +import type { JourneyStageData } from './types'; + +type ScrollProgressRailProps = { + stages: JourneyStageData[]; + activeIndex: number; +}; + +export default function ScrollProgressRail({ stages, activeIndex }: ScrollProgressRailProps) { + return ( + + ); +} diff --git a/src/components/visual-journey/ThoughtBubble.tsx b/src/components/visual-journey/ThoughtBubble.tsx new file mode 100644 index 0000000..c5c264a --- /dev/null +++ b/src/components/visual-journey/ThoughtBubble.tsx @@ -0,0 +1,29 @@ +import { motion, useReducedMotion } from 'framer-motion'; + +type ThoughtBubbleProps = { + text: string; + index?: number; +}; + +export default function ThoughtBubble({ text, index = 0 }: ThoughtBubbleProps) { + const reduceMotion = useReducedMotion(); + + return ( + + {text} + + ); +} diff --git a/src/components/visual-journey/VisualJourneyCharacter.tsx b/src/components/visual-journey/VisualJourneyCharacter.tsx new file mode 100644 index 0000000..515b6d4 --- /dev/null +++ b/src/components/visual-journey/VisualJourneyCharacter.tsx @@ -0,0 +1,119 @@ +import { motion, useReducedMotion } from 'framer-motion'; +import type { JourneyStageData } from './types'; + +type VisualJourneyCharacterProps = { + stage: JourneyStageData; + progress: number; + mobile?: boolean; +}; + +const stageTints: Record = { + college: 'from-neon/20 via-electric/20 to-transparent', + 'first-games': 'from-electric/20 via-neon/15 to-transparent', + 'hyper-casual': 'from-neon/30 via-neon/5 to-transparent', + publisher: 'from-electric/30 via-neon/15 to-transparent', + performance: 'from-neon/30 via-cyan-500/20 to-transparent', + xr: 'from-fuchsia-500/25 via-neon/15 to-transparent', + current: 'from-neon/35 via-electric/15 to-transparent', +}; + +function StageOverlay({ visualState }: { visualState: JourneyStageData['visualState'] }) { + if (visualState === 'xr') { + return ( + <> +
+
+
+ + ); + } + + if (visualState === 'performance' || visualState === 'current') { + return ( + <> +
+
+
+
+
+
+
+
+
+
+
+ + ); + } + + if (visualState === 'publisher') { + return ( +
+ {['Voodoo', 'Lion Studios', 'Supersonic'].map((chip) => ( + + {chip} + + ))} +
+ ); + } + + if (visualState === 'hyper-casual' || visualState === 'first-games') { + return ( + <> +
+
+
+ + ); + } + + return ( + <> +
+
+ + ); +} + +export default function VisualJourneyCharacter({ stage, progress, mobile = false }: VisualJourneyCharacterProps) { + const reduceMotion = useReducedMotion(); + + return ( +
+
+
+ + + + +
+
+
+
+
+
+ + + + + +

{stage.phase}

+
+ ); +} diff --git a/src/components/visual-journey/VisualJourneyHero.tsx b/src/components/visual-journey/VisualJourneyHero.tsx new file mode 100644 index 0000000..39b6f45 --- /dev/null +++ b/src/components/visual-journey/VisualJourneyHero.tsx @@ -0,0 +1,44 @@ +import { motion } from 'framer-motion'; +import SmartLink from '../SmartLink'; + +export default function VisualJourneyHero() { + return ( +
+
+
+ +

+ Visual Journey +

+

+ An Interactive Resume Through Real-Time Systems +

+

+ From game prototypes to profiler-backed XR performance engineering — a visual journey through 13+ years of + Unity, rendering, and frame-time optimization. +

+ +
+ + Start the journey + + + View standard resume + +
+
+
+
+ ); +} diff --git a/src/components/visual-journey/VisualJourneyPage.tsx b/src/components/visual-journey/VisualJourneyPage.tsx new file mode 100644 index 0000000..9ddc213 --- /dev/null +++ b/src/components/visual-journey/VisualJourneyPage.tsx @@ -0,0 +1,159 @@ +import { useEffect, useState } from 'react'; +import { motion, useScroll, useTransform } from 'framer-motion'; +import SmartLink from '../SmartLink'; +import JourneyStage from './JourneyStage'; +import ScrollProgressRail from './ScrollProgressRail'; +import VisualJourneyCharacter from './VisualJourneyCharacter'; +import type { JourneyStageData } from './types'; + +const journeyStages: JourneyStageData[] = [ + { + id: 'journey-stage-college', + phase: 'College & foundations', + title: 'Learning how systems move', + description: 'Built the foundation in programming, logic, and real-time interaction.', + bubbles: ['C#', 'Unity', 'Game loops', 'Math', 'Problem solving'], + visualState: 'college', + }, + { + id: 'journey-stage-first-games', + phase: 'Early game development', + title: 'From idea to playable loops', + description: 'Shipped early prototypes and learned rapid iteration, controls, and game feel.', + bubbles: ['Prototype', 'Iteration', 'Game feel', 'Retention'], + visualState: 'first-games', + }, + { + id: 'journey-stage-mobile-production', + phase: 'Hyper-casual production', + title: 'Mobile production with measurable outcomes', + description: 'Scaled prototype throughput while improving retention and reducing acquisition cost.', + bubbles: ['Publisher prototypes', 'Fast iteration loops', 'Mobile systems'], + metrics: ['D1 retention improved 27% → 37%', 'CPI reduced to $0.37'], + visualState: 'hyper-casual', + }, + { + id: 'journey-stage-publisher', + phase: 'Publisher collaboration', + title: 'Market-tested gameplay delivery', + description: 'Collaborated with major publishers and turned validated concepts into public releases.', + bubbles: ['50+ commissioned prototypes', '100+ public releases', 'Market-tested gameplay'], + chips: ['Voodoo', 'Lion Studios', 'Supersonic'], + visualState: 'publisher', + }, + { + id: 'journey-stage-performance', + phase: 'Performance engineering', + title: 'Profiler-backed optimization systems', + description: 'Focused on deterministic frame budgets, bottleneck isolation, and production-safe optimizations.', + bubbles: ['CPU/GPU bottleneck isolation', 'GC-free systems', 'Frame pacing', 'Profiler-backed experiments'], + metrics: ['11ms / 16.6ms frame budgets'], + visualState: 'performance', + }, + { + id: 'journey-stage-xr', + phase: 'XR rendering research', + title: 'Rendering cost analysis under XR constraints', + description: + 'Ran XR rendering studies to isolate overdraw amplification, stereo pipeline costs, and tile-based GPU behavior.', + bubbles: ['Stereo rendering', 'Overdraw amplification', '+7.27ms GPU cost isolated', 'Tile-based GPU awareness'], + visualState: 'xr', + }, + { + id: 'journey-stage-current', + phase: 'Current positioning', + title: 'Senior Real-Time Performance Engineer', + description: + 'Now focused on Unity rendering, frame pacing, XR optimization, and evidence-driven performance engineering.', + bubbles: ['Unity Rendering', 'Frame Pacing', 'XR Optimization', 'Performance case studies available'], + visualState: 'current', + }, +]; + +function useIsMobile() { + const [isMobile, setIsMobile] = useState(() => + typeof window !== 'undefined' ? window.matchMedia('(max-width: 767px)').matches : false, + ); + + useEffect(() => { + if (typeof window === 'undefined') return; + + const query = window.matchMedia('(max-width: 767px)'); + const update = () => setIsMobile(query.matches); + update(); + query.addEventListener('change', update); + + return () => query.removeEventListener('change', update); + }, []); + + return isMobile; +} + +export default function VisualJourneyPage() { + const [activeIndex, setActiveIndex] = useState(0); + const isMobile = useIsMobile(); + const { scrollYProgress } = useScroll(); + const railProgress = useTransform(scrollYProgress, [0, 1], [0.92, 1]); + const stageProgress = (activeIndex + 1) / journeyStages.length; + + return ( +
+
+ + +
+
+ {journeyStages.map((stage, index) => ( + + ))} + +
+

Want the standard version?

+

+ Open the resume, case studies, or GitHub evidence behind the visual journey. +

+ +
+

• 13+ years real-time systems experience

+

• 10+ years Unity

+

• Mobile games, rendering, XR, frame pacing

+

• Published with Voodoo, Lion Studios, Supersonic

+

• Performance case studies available

+
+ +
+ + View Resume + + + Download Resume + + + View Case Studies + + + View GitHub + + + Contact + +
+
+
+ + + + +
+
+
+ ); +} diff --git a/src/components/visual-journey/types.ts b/src/components/visual-journey/types.ts new file mode 100644 index 0000000..05005a7 --- /dev/null +++ b/src/components/visual-journey/types.ts @@ -0,0 +1,19 @@ +export type VisualState = + | 'college' + | 'first-games' + | 'hyper-casual' + | 'publisher' + | 'performance' + | 'xr' + | 'current'; + +export interface JourneyStageData { + id: string; + phase: string; + title: string; + description: string; + bubbles: string[]; + metrics?: string[]; + chips?: string[]; + visualState: VisualState; +} diff --git a/src/lab/pages/FramePacingLabPage.tsx b/src/lab/pages/FramePacingLabPage.tsx index bdadefe..7934f61 100644 --- a/src/lab/pages/FramePacingLabPage.tsx +++ b/src/lab/pages/FramePacingLabPage.tsx @@ -8,7 +8,7 @@ export default function FramePacingLabPage() { diff --git a/src/pages/NotFoundPage.tsx b/src/pages/NotFoundPage.tsx index d8a5e0b..b3b2eb4 100644 --- a/src/pages/NotFoundPage.tsx +++ b/src/pages/NotFoundPage.tsx @@ -8,7 +8,7 @@ export default function NotFoundPage() {

404

Page not found

diff --git a/src/pages/VisualJourney.tsx b/src/pages/VisualJourney.tsx new file mode 100644 index 0000000..275ff3b --- /dev/null +++ b/src/pages/VisualJourney.tsx @@ -0,0 +1,21 @@ +import { Seo } from '../components/Seo'; +import VisualJourneyHero from '../components/visual-journey/VisualJourneyHero'; +import VisualJourneyPage from '../components/visual-journey/VisualJourneyPage'; + +export default function VisualJourney() { + return ( +
+ + + ); +} From 2748f046d328f9af30b05f79a7ec5ed83e94815f Mon Sep 17 00:00:00 2001 From: James De Raja Date: Tue, 28 Apr 2026 21:56:48 +0530 Subject: [PATCH 2/2] Add visual resume link to top navigation --- src/components/Navbar.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index fda87bc..f992e98 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -77,6 +77,12 @@ export default function Navbar() { {/* Actions */}
+ + Visual Resume +
+ setMobileOpen(false)} + > + Visual Resume + {navItems.map((item) => (