~/work/spicy-dan/
Spicy Dan
Built a custom Shopify jewelry storefront where Liquid product data, a Three.js configurator, bundle cart behavior, and mobile media flows worked as one product system.

- Role
- A Unified Whole · Software Engineer
- Period
- Sep 2025 – Jan 2026
- System
- interactive commerce
The storefront needed its own language
Spicy Dan was not a stock theme with different colors. The brand depended on motion, bold typography, jewelry media, and an unusual path from browsing to configuration.
Jeff Visoky created the brand and web design for Spicy Dan. He gave me the visual system and page designs; I translated them into the Shopify theme, commerce behavior, media tooling, and Build-A-Drip configurator.
The repository began as a Shopify theme skeleton, but the useful early work was defining a system that could carry the visual direction without scattering one-off values through Liquid and CSS. I documented color, type, spacing, radius, button, card, and layout rules, then built reusable sections and snippets around those decisions.
That foundation covered the ordinary commerce surfaces first: header and footer, homepage sections, collection and product cards, product detail, cart, newsletters, and supporting content. Starting there mattered. A configurator can attract attention, but it still lands inside a store where navigation, availability, media, and checkout must behave normally.
The theme had to serve two forms of change. I could revise the interaction in code, while the merchant could reorder sections, select collections, update media, and change content through Shopify’s schema. The goal was a distinct storefront that remained operable after handoff, not a static campaign page trapped in a developer workflow.
Liquid became the boundary between commerce and interaction
Build-A-Drip turned Shopify product data into a browser-side configuration model.
Liquid emitted the collections, variants, availability, prices, handles, media, and selected metadata that JavaScript needed. The browser then organized that data into steps such as chain, bead, carabiner, or charm selection. That bridge was more important than the visual controls. If a product was hidden, unavailable, renamed, or mapped to the wrong model node, the interface could look correct while offering a combination the store could not sell.
I kept Shopify as the commerce authority. The configurator owned presentation and temporary selection state, but products and variants remained the source for price and availability. URL parameters could restore or share a configuration, and the overlay could open directly when those parameters were present. State changes updated the visible selection, price, model, and final cart payload together.
This was a useful pattern for platform work in miniature: adapt a system’s real data into an interaction-specific model, but do not create a second authority just because the browser needs a friendlier shape.
The 3D viewer was a stateful product surface
The model could not behave like a looping decoration because every visible part represented a sellable decision.
The viewer used Three.js with GLTF loading and compressed model assets. Product choices mapped to scene nodes, meshes, and materials. Changing an option could hide one part, reveal another, recolor a material, reset the camera, or preserve a compatible selection from the previous step. The implementation had to wait for the model to load without letting a late response overwrite a newer customer choice.
Asset handling became part of the programming work. Draco or Meshopt compatibility, texture size, material attributes, environment lighting, and model-specific fixes affected whether jewelry looked believable and whether the page loaded acceptably on a phone. A technically valid GLB could still arrive with a broken base color, an unsupported attribute, or a material recipe that looked wrong under the storefront’s lighting.
I used explicit load state, cached model resources where appropriate, and isolated model adapters rather than spreading node names through every click handler. The scene remained coupled to the product because that coupling was real, but the boundary made it possible to reason about which layer owned product state, scene state, and rendering behavior.
A configuration had to survive the cart
The interaction was incomplete until Shopify could receive a coherent set of line items.
A finished piece could include several products or variants that needed to travel together. The add-to-cart path built the group deliberately, attached configuration properties, handled optional parts, and preserved enough information for the cart to present the bundle as one customer decision rather than unrelated inventory lines.
That forced the configurator to account for ordinary commerce failure states. A selected option could sell out. A variant could be hidden. The cart request could fail after the viewer looked finished. The final-sale acknowledgement and purchase controls needed real disabled, loading, and error behavior. Repeated taps on mobile could not create duplicate groups.
The implementation made one thing clear: visual configuration and commerce state were the same feature. The model preview could help a customer understand the product, but the actual engineering boundary ended only after the selected configuration became a valid Shopify cart operation.
Mobile changed the interaction model
The desktop version had room for a product, controls, context, and navigation at once. A phone did not.
The mobile flow became step-oriented, with explicit navigation, a viewer that did not monopolize the viewport, and purchase controls that remained reachable. Overlays needed scroll locking and predictable close behavior. Camera defaults, touch gestures, option rails, and text density all required separate tuning rather than scaled desktop values.
The rest of the storefront changed with it. Product cards needed mobile video mappings, collection and cart surfaces needed stable heights, and navigation could not assume hover. Commit history shows the less visible corrections that made the designed experience usable: add-to-cart fixes, selector state, camera resets, sold-out behavior, newsletter and sale states, and media changes across breakpoints.
Those iterations made the project feel like product engineering rather than a single visual build. The design had to keep its identity while the underlying interaction changed for the device.
Media operations belonged in the repository too
A media-heavy storefront creates repetitive work that should not depend on careful manual clicks forever.
I built scripts for listing, downloading, uploading, and remapping product videos through Shopify’s APIs. Desktop and mobile assets could target separate metafields, staged uploads handled the transfer path, and mapping files kept product-to-media relationships reviewable. Rate limiting and first-frame handling made the workflow practical for batches rather than one upload at a time.
This tooling was not visible in the storefront, but it reduced a class of content errors and made later media changes cheaper. It also kept operational knowledge close to the theme instead of living only in an admin session or a private message.
The same principle connected the whole project: the polished customer experience depended on explicit data and workflows underneath it. Brand design, Liquid schemas, 3D state, cart semantics, and media operations all had to agree.
Build-A-Drip in hindsight
The hard part of interactive commerce is preserving one product decision across several technical systems.
Spicy Dan moved through Shopify data, Liquid rendering, JavaScript state, a Three.js scene, responsive controls, and the cart. Any layer could display a plausible result while disagreeing with another. The work became reliable when each boundary had an explicit contract and Shopify remained authoritative for what could actually be purchased.
It also changed how I think about visual engineering. Rendering quality and interaction polish mattered, but they were strongest when attached to careful identifiers, state transitions, fallbacks, and operating tools. The experience felt custom because the software understood the product, not because the page ignored the commerce platform underneath it.
- Let Shopify own product, variant, price, and availability truth.
- Model scene state separately, then map it deliberately to commerce identifiers.
- Treat bundled cart behavior and failure states as part of the configurator.
- Automate media operations when asset volume makes manual work error-prone.