How we built Gallarix
By Eliran Yihye, Founder and CEO.
Gallarix is built and run by Cuanto Labs, a software studio in Fort Lauderdale, Florida. It is a gallery operating system for digital art: collectors walk a 3D viewing room, directors manage inventory, events and campaigns from a dashboard, and a Gemini-powered concierge answers questions from the live inventory. It is a React single page app that renders its 3D scene in the browser with WebGL and prerenders its public marketing pages to static HTML.
The problem
A gallery has two audiences with opposite needs. Collectors want an immersive, fast experience on whatever device they have, usually a phone. Directors want dense tools: inventory, provenance, events, mailing campaigns and sales analytics. Most gallery software serves the second audience and treats the first as a product grid. We wanted one codebase that does both, and a viewing room that feels like a place rather than a slideshow.
Architecture
The app is TypeScript on React 18 and Vite 6, styled with Tailwind CSS v4. The 3D room uses React Three Fiber and drei on top of Three.js, the dashboard charts use Recharts, and shared state lives in a Zustand store (store.ts). Vite splits the bundle into react-vendor, three-vendor and recharts-vendor chunks in vite.config.ts, so a visitor who never opens the dashboard never downloads the chart library.
Data model
The core types are in types.ts. An Artwork carries title, artist, year, medium, dimensions, price, tags and a status of Available, Reserved or Sold, plus optional provenance entries, a certificate of authenticity flag and condition notes. Events carry capacity, registered count and ticket price so the dashboard can compute revenue, and campaigns are Draft, Scheduled or Sent. The store seeds all of this with sample data, and keeps the collector's favorites in browser local storage under a single key as a list of artwork ids.
Shipping is estimated on the client in PublicGallery.tsx: the dimensions string is parsed into width and height, the area sets a base cost with a floor of 50, and three options (standard, express, white-glove) scale from it.
One scene, three device profiles
The viewing room is the expensive part, about 850 lines in components/virtualgallery.tsx. A WebGL scene that is fine on a desktop GPU can stutter a phone, so utils/deviceDetection.ts classifies the device and returns a profile. Desktop gets shadows, reflections, 1024 pixel textures, up to 10 animated objects and a pixel ratio capped at 2. Tablets drop shadows, use 512 pixel textures, 5 animated objects and a pixel ratio of 1.5. Phones also drop reflections and antialiasing and request a low-power GPU.
When a loaded texture is larger than the profile allows, the scene redraws it onto a smaller 2D canvas and uses that as the texture source, with mipmaps off and linear filtering. Controls follow the same split: AdaptiveControls uses touch controls on phones and tablets, and keyboard movement on desktop with a toggle to touch.
The scene itself uses drei helpers: MeshReflectorMaterial for the polished floor, Environment for lighting, Text for in-scene labels, and a Suspense fallback while textures load. The canvas turns alpha off, since a gallery room never needs a transparent background, and takes its pixel ratio, antialiasing and power preference straight from the device profile. A separate NavigationHandler uses a Three.js raycaster to turn taps into camera moves, and animates the camera to the new position rather than jumping.
The director dashboard
The dashboard in components/Dashboard.tsx is where the same store becomes an operating tool. Inventory can be added and edited, events track registrations against capacity, and campaigns are tracked as draft, scheduled or sent. Sales and engagement views are drawn with Recharts bar, line and pie charts over the store data. Because it reads the same Zustand state as the collector view, marking a work as reserved or sold shows up immediately in both places without any synchronization code.
The concierge
services/geminiService.ts wraps the @google/genai SDK. The chat function builds its system instruction from the live inventory: available works are listed with artist, medium and exact price, sold and reserved works are listed separately, and the rules tell the model to recommend only available pieces and to quote exact prices. Description, pricing, event copy and email drafts use a faster model. The client is created lazily, and if no key is configured the functions return a polite message instead of throwing.
Trade-offs
The AI key ships in the bundle. Vite's define inlines the Gemini key at build time, which makes a static deployment simple but means the key must be treated as public and restricted accordingly. A server-side proxy is the obvious hardening for a production gallery.
Hash routes hide content from crawlers. The privacy page, terms and journal use hash routes in App.tsx, which is easy in a single page app but invisible to a crawler. So the pages that must be found, the home page, the about page and this article, are real paths prerendered by vite-prerender-plugin: prerender.tsx renders hook-free components with renderToString and injects the title, canonical link and JSON-LD into each page head. The client then mounts over that HTML. Vercel serves the static file first and falls back to index.html for everything else.
User agent sniffing for profiles. Device classification reads the user agent and viewport, and caches the result until the window size changes by more than 50 pixels, with a 250 millisecond debounce on resize. It is cheap and predictable, and less precise than measuring real GPU capability.
What's next
The director dashboard still has placeholder panels for contacts, invoices and settings, marked as coming soon in components/Layout.tsx. They are the next screens to build, and they will need the persistence layer the sample data currently stands in for.
FAQ
- What is Gallarix?
- Gallarix is a gallery operating system for digital art: a collector view with a 3D viewing room, a director dashboard for inventory, events and campaigns, and a Gemini-powered concierge.
- How does the 3D viewing room run on a phone?
- The room is rendered with React Three Fiber. A device profile lowers texture size, pixel ratio, shadows and animated object counts on phones and tablets, and swaps keyboard controls for touch controls.
- Is Gallarix server rendered?
- The marketing home page, the about page and this article are prerendered to static HTML at build time with vite-prerender-plugin, so crawlers that do not run JavaScript can read them. The interactive app mounts over that HTML in the browser.
- Where does the gallery data live?
- In this version the inventory, events and campaigns live in a Zustand store seeded with sample data, with favorites kept in browser local storage. There is no backend database yet.