Racerz is a production-grade, full-stack web application for discovering, comparing, and experiencing premium motorcycles. Built for riders and enthusiasts in mind, it combines a curated showroom catalog backed by PostgreSQL with high-fidelity browser-synthesized engine acoustics, a side-by-side telemetry comparison cockpit, an interactive 3D rider profile, and a retrieval-augmented (RAG) AI concierge that answers questions with zero API keys — fully local and free.
The application is engineered as a single deployable unit: a server-rendered React frontend (TanStack Start + React Router), type-safe server functions, Supabase for database and authentication, pgvector for vector search, and Cloudflare Workers as the edge runtime.
- What is Racerz?
- Key Features
- Technology Stack
- System Architecture
- Project Structure
- Database Schema
- Getting Started
- Environment Variables
- Available Scripts
- Deployment
- Contributing
- License
Racerz is not a typical listing site. It is a high-fidelity, interactive experience designed to feel like an active racing garage rather than an e-commerce catalog. Visitors can:
- Browse a curated catalog of six motorcycle categories — sports, naked, adventure, cruisers, electric, and scooters.
- "Start the engine" on any machine and hear a procedurally synthesized exhaust note generated in the browser via the Web Audio API (no audio files, no latency).
- Compare up to four superbikes head-to-head across four-and-a-half dozen telemetry specs, with radar charts and auto-badged winners.
- Open an interactive 3D rider-license ID card with parallax tilt and tachometer rings.
- Ask the built-in AI concierge — R Assistant — questions about the catalog and get answers grounded in real showroom data, running entirely locally.
The project is intentionally dependency-light at runtime: the AI layer runs embeddings and retrieval completely offline, meaning the app has no recurring API costs for its flagship concierge feature.
- No audio assets. Exhaust sounds are synthesized in real time with the Web Audio API, removing bulky media files from the bundle.
- Engine-type-specific acoustics, each modeled with distinct oscillator and waveform designs:
- Sports (superbike) — detuned parallel sawtooth oscillators for a high-frequency inline-four / V4 scream.
- Cruisers — an ultra-low-frequency V-twin "potato-potato" thump modulated by a custom 6.5 Hz LFO.
- Naked — a throaty, performance-tuned triple/quad exhaust note.
- Electric — clean sine/triangle pitch-sweep emulating an EV motor wind-up.
- Tachometer rev sweeps — a circular speed-dial visualizer; hovering or dragging across the gauge sweeps synthesizer frequency up to 12,000 RPM.
- 60fps equalizer visualizer — a canvas
AnalyserNodedrawing real-time frequency heights viarequestAnimationFrame.
- Compare up to 4 motorcycles simultaneously with sticky, scroll-locked column headers.
- Fuzzy search with instant autocomplete and cached recent searches.
- 14 telemetry specifications compared: displacement, power, torque, top speed, curb weight, seat height, fuel capacity, fuel consumption, transmission, and more.
- Recharts radar spider charts overlaying Touring, Comfort, Speed, and Range.
- A "Differences only" filter hides identical rows to surface distinct specs, and winner badges auto-highlight the lightest, cheapest, fastest, and most powerful machines.
- Apple/Threads-style parallax card that tilts in 3D based on cursor position using Framer Motion springs.
- Interactive SVG tachometer rings that rev and redline on hover, framing the rider avatar.
- Semantic retrieval over a Supabase pgvector index with HNSW-accelerated cosine similarity.
- 100% local and free embeddings via
@huggingface/transformers(Xenova/all-MiniLM-L6-v2, 384-dim ONNX). No API keys, no payments, no external LLM calls. - Hybrid retrieval — Reciprocal Rank Fusion (RRF) merges local vector search with a keyword + prefix-semantic matcher.
- Conversation-aware — follow-up turns are normalized into standalone queries.
- Retrieval-grounded answers — bike quotes return real showroom specs from the indexed catalog; out-of-scope questions gracefully hand off to a human.
- Crawler + indexer — a background sync crawls static routes and
custom_bikesrecords, chunking content and upserting embeddings idempotently. - TanStack Router-safe markdown — links in chat resolve through the client router without full-page reloads.
- Admin index console — authorized users can trigger a manual KB crawl from the chat header with toast status feedback.
| Layer | Technology | Role |
|---|---|---|
| Meta-framework | TanStack Start | Full-stack SSR + type-safe server functions |
| Routing | TanStack Router | File-based, type-safe client routing |
| UI framework | React 19 | Component rendering & interactions |
| Styling | Tailwind CSS 4 | Utility-first styling system |
| Data fetching | TanStack Query | Server state & caching on the client |
| Database & Auth | Supabase | PostgreSQL, RLS policies, email auth |
| Vector search | pgvector | 384-dim embedding similarity (HNSW) |
| Embeddings (local) | @huggingface/transformers | ONNX feature extraction, no API cost |
| Animations | Framer Motion | Cinematic micro-interactions & 3D parallax |
| Charts | Recharts | Radar/spider telemetry visualizations |
| Audio synthesis | Web Audio API | Procedural engine sound synthesis |
| UI primitives | Radix UI | Accessible, unstyled component primitives |
| Forms & validation | react-hook-form + Zod | Schema-validated forms |
| Runtime | Cloudflare Workers | Edge serverless deployment |
| Build tool | Vite | Dev server & production bundling |
| Package manager | Bun / npm | Scripts & dependency management |
┌─────────────┐ Server Functions / RPC ┌─────────────────────┐
│ Browser │ <────────────────────────────────> │ TanStack Start SSR │
│ (React 19) │ type-safe createServerFn calls │ Edge/Server Runtime│
└─────┬───────┘ └──────────┬──────────┘
│ Web Audio synthesis, Recharts, 3D parallax │
│ │
│ ┌─────────────────────────────┼───────────────────┐
│ │ │ │
│ ┌─────▼──────┐ ┌───────▼────────┐ ┌───────▼────────┐
│ │ Supabase │ │ pgvector (HNSW)│ │ transformers.js │
└──────────────────│ PostgreSQL │────────────> │ kb_embeddings │<──│ local ONNX │
│ + RLS Auth │ │ match_kb_...() │ │ embeddings │
└────────────┘ └──────────────────┘ └────────────────┘
Request flow
- The browser client calls type-safe server functions (
createServerFn) generated by TanStack Start. - Server functions query Supabase — public catalog data via RLS-protected tables, embeddings via the
match_kb_embeddings(...)SQL function. - For the concierge, the server generates a 384-dim query embedding locally with the ONNX model, retrieves the top relevant chunks from the pgvector index, and fuses results with keyword matching (RRF) to produce a grounded answer.
- All rendering is server-side for SEO and performance, then hydrated on the client.
The codebase follows a TanStack Start convention: file-based routes under src/routes, UI components under src/components, server/prisma-free data access under src/lib, and Supabase integrations isolated in src/integrations.
Bike_Store/
├── .github/
│ └── workflows/
│ └── deploy.yml # CI: build + deploy to Cloudflare Workers
├── public/
│ └── favicon.ico
├── scripts/
│ ├── run-migrations.mjs # Migration runner (Management API or Postgres)
│ ├── seed_motorcycles.ts # Seeds the catalog from API Ninjas into Supabase
│ ├── upload_and_assign_images.ts # Uploads + assigns CDN images to bikes
│ └── eval-rag.ts # Offline evaluation harness for the RAG pipeline
├── src/
│ ├── assets/ # Local fallback bike imagery
│ ├── components/
│ │ ├── site/ # App-shell & feature components
│ │ │ ├── Navbar.tsx # Top navigation + auth-aware links
│ │ │ ├── Footer.tsx
│ │ │ ├── BikeCard.tsx # Showroom grid card with "Start ignition"
│ │ │ ├── BikeActions.tsx # Wishlist / enquiry actions
│ │ │ ├── EngineDashboardHUD.tsx # Floating tachometer + equalizer dock
│ │ │ ├── RAssistantChat.tsx # R Assistant AI concierge widget
│ │ │ ├── ProfileScene3D.tsx # 3D rider-license trackpass card
│ │ │ ├── BrandMarquee.tsx # Brand ticker
│ │ │ ├── CatalogUnavailable.tsx # Graceful empty-state
│ │ │ ├── EstimatedPriceNote.tsx # Pricing disclosure component
│ │ │ ├── ScrollProgress.tsx # Reading progress indicator
│ │ │ ├── PageTransition.tsx # Route transition animations
│ │ │ └── Counter.tsx # Animated stat counters
│ │ └── ui/ # Radix-based shadcn/ui primitives (button, card, dialog, ...)
│ ├── data/
│ │ └── bikes.ts # Category taxonomy + client-safe constants
│ ├── hooks/
│ │ ├── use-auth.tsx # Auth context provider (sign in / sign up / sign out)
│ │ └── use-mobile.tsx # Responsive breakpoint hook
│ ├── integrations/
│ │ └── supabase/
│ │ ├── client.ts # Browser Supabase client (anon key)
│ │ ├── client.server.ts # Server Supabase admin client (service role)
│ │ ├── auth-middleware.ts # Bearer-token auth verification for server calls
│ │ ├── auth-attacher.ts # Attaches session bearer tokens to RPCs
│ │ └── types.ts # Generated database types
│ ├── lib/
│ │ ├── engine-sound.ts # Procedural Web Audio synthesizer
│ │ ├── chatbot.server.ts # RAG pipeline: crawler, embeddings, retrieval
│ │ ├── chatbot.functions.ts # TanStack server functions for the concierge
│ │ ├── motorcycles.server.ts # DB queries for the catalog
│ │ ├── motorcycles.functions.ts # TanStack server functions for the catalog
│ │ ├── error-capture.ts # SSR error capture
│ │ ├── error-page.ts # Branded error page renderer
│ │ └── utils.ts # Shared helpers (cn, ...)
│ ├── routes/ # File-based routes
│ │ ├── __root.tsx # Root shell: meta, layout, providers, 404
│ │ ├── index.tsx # Home / landing page
│ │ ├── bikes.index.tsx # Catalog with search & category filters
│ │ ├── bikes.$bikeId.tsx # Individual bike detail page
│ │ ├── compare.tsx # Telemetry comparison cockpit
│ │ ├── about.tsx / contact.tsx # Static pages
│ │ ├── login.tsx / signup.tsx # Email authentication pages
│ │ ├── _authenticated.tsx # Auth-guarded route group
│ │ ├── _authenticated/dashboard.tsx# Rider dashboard
│ │ └── _authenticated/admin.tsx # Admin management console
│ ├── routeTree.gen.ts # Generated route tree (do not edit)
│ ├── router.tsx # Router factory with QueryClient context
│ ├── server.ts # SSR entry with error normalization
│ ├── start.ts # App bootstrap entry
│ └── styles.css # Tailwind + design tokens
├── supabase/
│ ├── config.toml # Local Supabase CLI config
│ └── migrations/ # SQL migrations (schema + RLS + vector index)
├── .env.example # Documented environment variable template
├── .dev.vars.example # Cloudflare secret variable template
├── wrangler.jsonc # Cloudflare Workers configuration
├── vite.config.ts # Vite + TanStack Start configuration
├── components.json # shadcn/ui configuration
├── package.json
└── tsconfig.json
Managed through SQL migrations in supabase/migrations/. Key tables and objects:
| Object | Purpose | RLS |
|---|---|---|
custom_bikes |
The motorcycle catalog (specs, prices, images, features) | Public read; admin write |
kb_embeddings |
RAG knowledge base: chunked content + 384-dim embeddings | Public read |
profiles |
User profile data, auto-created on signup | Owner / admin |
user_roles |
App role assignment (admin / user) with has_role() helper |
Owner / admin |
wishlist |
Saved bikes per user | Owner only |
enquiries |
Buyer enquiries sent to staff | Owner / admin |
bookings |
Test-ride bookings | Owner / admin |
Notable database features:
public.match_kb_embeddings(query_embedding, match_threshold, match_count)— pgvector similarity search (HNSW index, cosine).- A
handle_new_user()trigger auto-createsprofilesand a defaultuserrole on signup. - A
custom_bikes_updated_attrigger maintains theupdated_atcolumn. - Row Level Security is enabled on every application table.
- Node.js v20 or newer
- Bun (recommended) or npm
- A Supabase project (database + auth)
- A Cloudflare account — only required for deployment
git clone https://github.com/Pyro-Warrior-1884/Bike_Store.git
cd Bike_Store
npm installCopy .env.example to .env and fill in your Supabase credentials (see Environment Variables). Never commit the real .env — it is already ignored by .gitignore.
npm run db:migrateThis enables pgvector, creates all tables listed above, configures Row Level Security, and installs the vector-search function.
bun scripts/seed_motorcycles.tsFetches real motorcycle data from the API Ninjas Motorcycles API, stores it in custom_bikes, and assigns category images from the local assets.
npm run devOpen http://localhost:3000.
Note: if the catalog is not yet seeded (and the database is unreachable), the UI renders a graceful
CatalogUnavailablestate instead of failing.
The application expects the following environment variables. Reference values only — obtain the real values from your Supabase project dashboard.
| Variable | Required | Description | Used by |
|---|---|---|---|
VITE_SUPABASE_URL |
Yes | Supabase project URL | Browser client |
VITE_SUPABASE_PUBLISHABLE_KEY |
Yes | Anon / publishable API key | Browser client |
VITE_SUPABASE_PROJECT_ID |
No | Project reference (used by some scripts) | Scripts |
SUPABASE_URL |
Yes | Server-side Supabase URL | Server / scripts |
SUPABASE_SERVICE_ROLE_KEY |
Yes* | Admin key (bypasses RLS — server-only, never ship) | Server functions |
SUPABASE_PUBLISHABLE_KEY |
No | Fallback anon key for server contexts | Server |
DATABASE_URL |
Scripts | Postgres connection string for migrations | Migration runner |
SUPABASE_DB_PASSWORD |
Scripts | Database password for migrations | Migration runner |
API_NINJAS_KEY |
Seed | API Ninjas Motorcycles API key | Seeding script |
* Required when the catalog/RAG server functions run against Supabase in production.
Security note: SUPABASE_SERVICE_ROLE_KEY bypasses Row Level Security and must only ever live in server-side contexts (Cloudflare secrets / GitHub Actions secrets). It must never be referenced from client code or committed to the repository.
See .env.example and .dev.vars.example for the full commented templates.
| Command | Description |
|---|---|
npm run dev |
Start the Vite dev server |
npm run build |
Production build |
npm run preview |
Preview the production build locally |
npm run deploy |
Build, then deploy to Cloudflare Workers |
npm run db:migrate |
Run Supabase migrations |
npm run db:push |
Push schema changes via the Supabase CLI |
npm run eval:rag |
Evaluate RAG retrieval quality offline |
npm run lint |
Run ESLint |
npm run format |
Format the codebase with Prettier |
Racerz deploys serverlessly to Cloudflare Workers.
-
Authenticate with Wrangler
bunx wrangler login
-
Configure runtime secrets
Copy
.dev.vars.exampleto.dev.vars, fill in server secrets (includingSUPABASE_SERVICE_ROLE_KEY), and upload them:bunx wrangler secret bulk .dev.vars
-
Build and deploy
npm run deploy
.github/workflows/deploy.yml builds and deploys on push to main. It requires the following GitHub repository secrets:
CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, VITE_SUPABASE_URL, VITE_SUPABASE_PUBLISHABLE_KEY, VITE_SUPABASE_PROJECT_ID, SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, SUPABASE_SERVICE_ROLE_KEY, API_NINJAS_KEY.
Contributions are welcome. To contribute:
- Fork the repository.
- Create a feature branch (
git checkout -b feature/amazing-feature). - Commit your changes with a clear message.
- Push to the branch and open a pull request.
Please keep commits scoped, follow the existing lint/format tooling (npm run lint, npm run format), and never commit environment files or secrets.
Distributed under the MIT License. See LICENSE for more information.