Rasveda Labs Case study · Portfolio

Sensory sommelier for cannabis

Describe how it should smell, taste and feel — find the match on any menu.

A private-by-design tasting room. Instead of “indica / sativa / hybrid” reductionism, SŌMA lets a member build a personal sensory profile — aromas, flavours, effects — then scans any dispensary menu and ranks every strain against that taste with a deterministic, fully explainable match engine.

Role
Product · Brand · Design · Full-stack engineering
Stack
Next.js 15 · React 19 · TypeScript · Prisma / Postgres
Status
Functional MVP · pre real-menu validation
The idea

A private tasting room, not a smoke-shop menu.

Every dispensary menu sorts flower by legacy taxonomy (indica / sativa / hybrid) and marketing copy (“euphoric, uplifting, creative”) — categories that don’t actually describe what a person’s palate wants tonight.

SŌMA borrows its language from wine and perfumery instead. Members describe how they want a flower to smell, taste and feel, and the app finds the ones that match — on any menu, with a written verdict for every result. Discover, experience, evolve.

What it does

Six rooms in the tasting house.

01

Sensory profiles

Up to three taste identities — e.g. Gas / Fuel bombs, Evening knock-out, Sweet tropics — each defined across aroma, flavour, effect, texture, potency and priority weights. A completeness ring unlocks matching at 60%.

02

Taste Match Engine

A deterministic scoring engine reading the same sensory vocabulary as the questionnaire, catalog and audit log. No black-box ML — every match ships a per-sense breakdown and a written verdict.

03

Menu analysis

Paste a dispensary menu as text — a deterministic parser pulls out grower, THC, price and weight — and get a ranked #1 / #2 / #3 board plus the full list, each row expandable into the analysis, with a compare-two side-by-side view.

04

Taste Blender

A virtual fourth profile that mixes two or three real ones with a bias slider and a bridge mode (min-across-worlds, so only strains strong in every world survive). Solves “I want both moods tonight.”

05

Harvest

An editorial catalog of 895 curated strains — sommelier-style curator notes, lineage, market aliases and a sensory family taxonomy (all in the language the engine reads from), with hand-drawn poster art for the headline strains.

06

Collection & Compare

A member’s shelf of tried and wishlisted flowers, plus a two-strain compare view that decomposes match scores by profile and by sense.

Zkittlez × Gelato

91%
Aroma92%
Flavour88%
Effect90%
Texture84%

“Candied tropical nose over a creamy gelato base; a soft, rounded evening lean. A near-perfect fit for your Sweet tropics profile.”

Under the hood

Selected engineering problems.

A

Deterministic, explainable scoring

Engine, questionnaire and strain data all draw from one canonical vocabulary object (VOCAB_VERSION), so a score is always reproducible from stored inputs — audit rows survive vocab migrations by pivoting on the version.

B

Anonymous → registered, no migration

Every visitor gets a signed anonymous UUID cookie; profile, history and audit log accumulate under it. Registering just sets username + password hash on the same row. HMAC session tokens validate in-process so a DB blip never silently logs a member out.

C

895-strain catalog performance

First paint started in “did it freeze?” territory. Fix: cache the assembled catalog (hundreds of thousands of comparisons no longer paid per cold start), trim the per-card payload sharply, paginate to 40 cards with IntersectionObserver infinite scroll, and add an instant skeleton.

D

Champagne-gold, built in CSS

Every gold surface is pure CSS — no image assets: masked hollow-ring borders, sculpted medallion rings with a debossed cream well, star-shine glints, and a 42% frosted card that lets the embossed leaf backdrop show through. All theme-aware.

Design language

Gilded, editorial, sommelier.

Gilded champagne-gold and frosted-cream cards on an embossed cannabis-leaf backdrop. Display in Fraunces (a high-contrast antiqua), UI in Inter. Every microcopy line reads like a sommelier note, not a marketing bullet — sensory-family gradients shift per strain territory (gas-og, garlic-funk, citrus-haze…).

Cream · #F6F3EE Champagne · #C8A76A Shine · #F6D88A
  • Fraunces + Inter
  • Frosted-cream cards
  • Embossed leaf backdrop
  • Per-strain poster art
  • Curator notes on 895 strains
The build

Stack.

Next.js 15 (App Router · React 19 · Server Components) with TypeScript and Tailwind. Prisma over PostgreSQL (Supabase); custom scrypt + HMAC-cookie auth (no auth library). OpenAI is optional — it only rewrites the written verdicts and infers off-catalog strain names; it never touches the scores. Tested with Node’s built-in test runner plus a stress harness that guards against “the illusion of intelligence.”

Next.js 15React 19TypeScript Tailwind CSSPrismaPostgreSQL · Supabase OpenAI (optional)node:testlucide-react

◆ ◆ ◆

Discover. Experience. Evolve.

SŌMA is live — step into the tasting room.