EcoThread is a HackPrinceton 2026 project focused on sustainable fashion discovery. It combines a personalized web app with a Chrome extension so users can find clothing they actually like, understand how sustainable an item is, and make lower-impact shopping decisions without leaving their normal browsing flow.
At a high level, the project does two things:
- The web app gives users a Pinterest-style feed of fashion listings based on their style preferences, lets them pin items to boards, and shows a sustainability score with plain-English reasoning.
- The Chrome extension scans supported shopping pages, extracts product details directly from the page, and shows a quick sustainability breakdown in the browser.
The current product-facing name in the UI is EcoThread. You may still see older names like ReWear or sustainable-fashion in package names, comments, or older docs. Those refer to the same project lineage.
Fashion sustainability information is usually buried, vague, or missing entirely. EcoThread tries to make that information visible at the moment a person is deciding what to buy.
The project is designed around a simple idea:
- help users discover clothing through taste and style, not just price
- surface sustainability signals in a way that feels approachable instead of academic
- reward secondhand and longer-lasting garments
- make lower-impact shopping feel easier, not harder
The web app lets a user:
- sign up or sign in with email
- create a style profile using tags, occasions, and free-form taste notes
- browse a personalized feed of fashion listings
- filter the feed by retailer
- open a product detail modal and view sustainability reasoning
- pin products to themed boards
- revisit saved boards and profile information later
The extension lets a user:
- turn the scanner on or off from a popup
- visit a supported product page
- automatically scrape product title, retailer, material, origin, price, image, and description when available
- score the item and display a compact sustainability breakdown
- see the score directly on the extension badge
- fall back to offline heuristics when no live scoring endpoint is configured
The repo also includes an optional Photon AI / tag-scanner prototype. That path is not required for the main web app or extension demo, but it shows how the team experimented with scanning clothing tags from messages and routing them through the same sustainability reasoning pipeline.
- A user creates an account and saves style preferences.
- The app turns those preferences into a recommendation query.
- A backend aggregation step searches supported retailers, validates the listing data, and caches results.
- The feed renders those listings in the web app.
- When a user opens or pins a product, the app calculates or retrieves a sustainability score.
- The user can save that product to a board for later.
- Separately, the extension can score an item directly from a retailer page while the user is browsing.
- React 18 for the UI
- TypeScript across the app and extension
- Vite for local development and production builds
- React Router for page routing
- TanStack Query for async data fetching, caching, and infinite feed pagination
- Tailwind CSS plus custom styling for the visual design system
- Chrome Extension Manifest V3
- React for the popup UI
- CRXJS Vite Plugin for building and hot-reloading the extension
- Content scripts to inspect retailer product pages
- Background service worker to coordinate scoring, caching, and badge updates
- Chrome storage for status and per-URL cache
- Supabase Auth for sign-in/sign-up
- Supabase Postgres for profiles, style preferences, boards, pins, product cache, and optional tag scans
- Supabase Edge Functions for recommendation retrieval, product aggregation, sustainability scoring, and tag analysis
- Row Level Security (RLS) so user-owned data stays scoped to the authenticated user
- Tavily for live search-based product aggregation across supported retailers
- Dedalus Labs for brand sustainability audit data
- K2-Think / IFM endpoint for deeper sustainability reasoning when configured
- Fallback heuristics so the app or extension can still function when live AI services are unavailable
- Photon / Spectrum / Bun for the message-based tag-scanner prototype in
photon-bot/ - A Kizaki-inspired schema/prototype path in
schema/and somesrc/backend files from an earlier iteration of the project
EcoThread is split into a few clear layers.
When a user picks style tags and occasions, the app builds a search phrase from those preferences. That phrase is used to request product recommendations from supported marketplaces. The recommendation logic lives in shared TypeScript utilities and is mirrored by the Supabase edge functions that power the live app.
The app does not rely on one retailer API. Instead, it aggregates product links from multiple retail or resale sources, normalizes titles, prices, URLs, and images, and filters out invalid or obviously irrelevant results. Listings are cached in the products table so repeated searches are faster and cheaper.
When a product needs a sustainability score, the backend first checks whether a usable score is already cached. If not, it can:
- look up brand-level sustainability context through Dedalus
- ask a reasoning model for a structured score and explanation
- fall back to retailer/material heuristics when live services are unavailable
This fallback behavior matters for a hackathon demo because it means the product can still show results even if the external model endpoint is down.
Boards and pins are stored in Supabase. When a user saves a product, the app stores a snapshot of the product plus the sustainability score at save time. That makes the board experience stable even if the source listing changes later.
The extension watches supported product pages, scrapes visible details from the DOM, sends the data to the background worker, and either:
- calls a live scoring API, or
- computes a heuristic sustainability breakdown locally
The popup then renders the result and the badge color updates to reflect the score.
+----------------------+
| Web App |
| React + Vite + TS |
+----------+-----------+
|
v
+----------------------+
| Supabase Auth/DB |
| profiles, boards, |
| pins, products |
+----------+-----------+
|
v
+----------------------+
| Supabase Functions |
| get-recommendations |
| aggregate-products |
| calculate-score |
| analyze-tag |
+----+-----------+-----+
| |
v v
+----------+ +---------+
| Tavily | | Dedalus |
+----------+ +---------+
|
v
+--------------+
| K2 / IFM API |
+--------------+
Browser page ---> content script ---> background worker ---> popup UI
\-> optional live score API
\-> otherwise local heuristic scoring
This is the practical map to the repo as it exists today:
| Path | Purpose |
|---|---|
src/ |
Main React web app source |
src/pages.tsx |
Main pages: auth, feed, boards, profile |
src/components/ |
Layout, chrome, cards, modals, visual system |
src/hooks/useProductFeed.ts |
Infinite feed query logic |
src/lib/rewear-store.ts |
Main client-side data access layer for the web app |
src/lib/recommendationQuery.ts |
Query-building logic from style preferences |
src/lib/listingValidation.ts |
Listing normalization, filtering, validation, and image/price extraction |
src/integrations/supabase/ |
Supabase client and generated types |
extension/ |
Chrome extension app |
extension/src/content/ |
Scrapers and DOM extraction logic |
extension/src/background/ |
Background service worker |
extension/src/popup/ |
Popup React UI |
extension/src/lib/api.ts |
Extension scoring client and heuristic fallback |
supabase/functions/ |
Edge functions used by the local/demo backend |
supabase/migrations/ |
Database schema and migrations |
schema/ |
Earlier schema/prototype artifacts |
photon-bot/ |
Optional message-based tag-scanner prototype |
The live recommendation pipeline currently targets:
- Depop
- Vinted
- eBay
- ThredUp
- Vestiaire Collective
- Whatnot
The extension manifest and scrapers currently include support for:
- Depop
- Vinted
- eBay
- ThredUp
- Vestiaire Collective
- Whatnot
- Zara
- H&M
- Shein
- ASOS
- Urban Outfitters
- Nordstrom
- Revolve
- Amazon
- Shopify stores
- Macy's
- Barbour
- Quince
- American Eagle
The extension also includes a generic scraper that tries to use standard product-page metadata when a dedicated site adapter is not present.
To run the project comfortably, install the following first:
- Node.js 20+ and npm
- Google Chrome or another Chromium browser that supports loading unpacked extensions
- Supabase CLI
- Docker Desktop or another Docker environment supported by local Supabase
Optional, only if you want the prototype tag-scanner path:
- Bun
- a Photon / Spectrum project configuration
- The root app and the extension both have
package.jsonfiles and build independently. - The PowerShell helper scripts in the repo expect a local Supabase CLI binary at
tools/supabase-cli/supabase.exe. - That binary is not currently checked into this repo, so the safest path for a new teammate is to install the official Supabase CLI globally and use the
supabasecommand directly. - If you prefer the helper scripts, you can place the CLI binary at the expected location yourself.
There are three main env surfaces to care about.
The frontend expects:
VITE_SUPABASE_URL=http://127.0.0.1:54321
VITE_SUPABASE_PUBLISHABLE_KEY=<your-supabase-anon-or-publishable-key>What this does:
VITE_SUPABASE_URLpoints the React app at your local or hosted Supabase projectVITE_SUPABASE_PUBLISHABLE_KEYallows the browser app to use Supabase auth and database access according to RLS policies
For the Supabase edge functions, the meaningful variables are:
TAVILY_API_KEY=<required-for-live-feed-results>
DEDALUS_API_KEY=<optional-brand-audit-context>
IFM_API_URL=<optional-openai-compatible-model-endpoint>
IFM_API_KEY=<optional-api-key-for-that-endpoint>
IFM_MODEL_ID=<optional-model-override-for-tag-scanner>What these control:
TAVILY_API_KEY: enables real retailer search aggregation for the web feedDEDALUS_API_KEY: enriches sustainability analysis with brand audit informationIFM_API_URL: enables live reasoning-based scoring instead of pure heuristic fallbackIFM_API_KEY: auth token for that model endpoint if neededIFM_MODEL_ID: optional override used by the tag-scanner prototype
Important behavior:
- Without Tavily, the feed will usually be empty unless you already have cached products in the database.
- Without Dedalus, scoring still works, but with less brand context.
- Without IFM, scoring falls back to retailer/material heuristics instead of live reasoning.
The extension supports:
VITE_REWEAR_API_BASE=Behavior:
- if left blank, the extension still works and uses built-in heuristic scoring
- if set, the extension will try to call
POST {VITE_REWEAR_API_BASE}/calculate-sustainability
Important caveat:
- The web app backend in this repo uses Supabase edge functions, which are not wired to the extension through this exact base-URL contract out of the box.
- In practice, the easiest way to demo the extension is to run it in heuristic mode unless you already have a compatible HTTP wrapper or deployed endpoint.
Only needed if you want to run the message-based tag-scanner prototype:
PHOTON_PROJECT_ID=<your-photon-project-id>
PHOTON_PROJECT_SECRET=<your-photon-project-secret>
SUPABASE_URL=<your-supabase-url>
SUPABASE_ANON_KEY=<your-supabase-anon-key>From the project root:
npm install
cd extension
npm install
cd ..Then create the env files described above.
If you do not care about the optional Photon bot, you can ignore photon-bot/ entirely.
Use the official Supabase CLI:
supabase startIf this is your first run, or if you want a clean local database with the repo migrations applied:
supabase db resetThis gives you:
- a local Postgres database
- local Auth
- local API endpoints
- the database schema from
supabase/migrations/
In a separate terminal:
supabase functions serve --env-file supabase/functions/.env.local --no-verify-jwtWindows note:
- the repo includes
serve-functions-local.ps1, but it expects a checked-in Supabase CLI binary - if you installed Supabase globally, using the raw
supabasecommand is simpler
In another terminal from the project root:
npm run devVite will print the local URL, usually something like http://localhost:5173.
When everything is running:
- open the Vite URL in your browser
- sign up with a new account, or use one of the seeded demo accounts below
- complete profile setup
- browse the feed
- pin a few products to boards
The app contains convenience demo accounts in code:
[email protected]/rewear-demo[email protected]/rewear-demo[email protected]/rewear-demo
If a demo account does not already exist, the app tries to bootstrap it automatically when you sign in with the expected password.
The extension can be run in two different modes.
This is the easiest and most reliable local demo path.
- leave
VITE_REWEAR_API_BASEblank inextension/.envorextension/.env.local - from
extension/, run:
npm run dev- open
chrome://extensions - enable Developer mode
- click Load unpacked
- choose the
extension/dist/folder - pin the extension in Chrome
- open a supported product page and turn the scanner on
In this mode, the extension scores items locally using heuristic logic from extension/src/lib/api.ts.
If you already have a compatible scoring endpoint:
- set
VITE_REWEAR_API_BASE - rebuild or rerun the extension
- load
extension/dist/
The extension expects a base URL that supports:
POST /calculate-sustainability
with a request body shaped like:
{
"item": {
"url": "...",
"retailer": "...",
"title": "...",
"brand": "...",
"price": 42,
"currency": "USD",
"image_url": "...",
"description": "...",
"material": "...",
"origin": "...",
"scraped_at": 1234567890
}
}and a response that can be normalized into the extension's SustainabilityBreakdown shape.
- Popup UI changes generally reload quickly with the Vite/CRXJS dev flow.
- Content-script and service-worker changes often require clicking the Reload button on the extension card in
chrome://extensions. - If the popup says no item was found, make sure you are on a supported product page rather than a category page or homepage.
If you want to demo the whole project as a system:
- install root and extension dependencies
- create the env files
- run
supabase start - run
supabase functions serve --env-file supabase/functions/.env.local --no-verify-jwt - run
npm run devfrom the repo root - run
npm run devfromextension/ - load
extension/dist/into Chrome - open the web app in one tab and supported retailer pages in another
Recommended demo split:
- use the web app to show onboarding, feed personalization, saving to boards, and sustainability detail
- use the extension to show in-context browsing and fast scoring on retail pages
The recommendation path is roughly:
- style tags and occasions are saved in
style_preferences - the app converts those preferences into a search query
get-recommendationsinvokesaggregate-productsaggregate-productsqueries supported retailer domains through Tavily- raw search results are validated and normalized
- valid results are cached in the
productstable - the frontend sorts, filters, and renders the results
Some useful implementation details:
- Depop and Vinted queries can be diversified into "top" and "bottom" fashion buckets to avoid overly repetitive results
- listing URLs are normalized and tracking parameters are stripped
- prices are extracted heuristically from retailer text when not explicitly available
- images are normalized and scored to prefer real product photos over logos, icons, or static assets
The scoring path is roughly:
- the frontend requests a sustainability score for a product
- the backend checks whether a non-fallback score already exists in
products - if needed, it optionally enriches the item with brand-audit context from Dedalus
- if configured, it calls a reasoning model endpoint for a structured score and explanation
- if the model is unavailable, it falls back to retailer/material heuristics
- the result is returned to the UI and sometimes cached back onto the product record
The score output shown to users typically includes:
- numeric score
- one-line explanation
- reasoning text
- estimated carbon comparison
- inferred fabric type
- inferred item condition
The main tables created by the Supabase migrations are:
| Table | Purpose |
|---|---|
profiles |
one profile per authenticated user |
style_preferences |
style tags, occasions, and free-form taste note |
boards |
user-created collections |
pins |
saved products linked to boards |
products |
cached listing aggregation results and score metadata |
tag_scans |
optional records created by the tag-scanner prototype |
Security model summary:
- user-owned tables use Row Level Security
- users can only read and mutate their own profiles, preferences, boards, and pins
- the
productstable is publicly readable but server-written tag_scansis intended for server-side use only
If you are new to the codebase, start here:
src/pages.tsxsrc/components/chrome.tsxsrc/hooks/useProductFeed.tssrc/lib/rewear-store.tssrc/lib/recommendationQuery.tssrc/lib/listingValidation.ts
extension/src/popup/Popup.tsxextension/src/background/service-worker.tsextension/src/content/content-script.tsextension/src/lib/api.tsextension/src/lib/messages.tsextension/src/lib/storage.ts
supabase/functions/get-recommendations/index.tssupabase/functions/aggregate-products/index.tssupabase/functions/calculate-sustainability/index.tssupabase/functions/analyze-tag/index.tssupabase/migrations/20260418165055_initial_schema.sql
This is optional and not needed for the core app demo.
The prototype flow is:
- a user sends a clothing-tag image through the Photon/Spectrum channel
photon-bot/index.tsforwards the image URL to the Supabaseanalyze-tagfunction- the edge function runs multimodal extraction/scoring logic
- the bot sends back a formatted sustainability response
Run it only if you have Bun and the Photon credentials set up:
cd photon-bot
npm install
bun run devIf Bun is not installed, skip this directory entirely.
Cause:
/.env.localis missing or incomplete
Fix:
- add
VITE_SUPABASE_URL - add
VITE_SUPABASE_PUBLISHABLE_KEY - restart Vite
Possible causes:
- Supabase edge functions are not being served
TAVILY_API_KEYis missing or invalid- you are using a clean DB with no cached products and no live search access
Fix:
- verify
supabase functions serveis running - verify
supabase/functions/.env.localcontains a valid Tavily key - try again after checking the function logs
Cause:
- the IFM/K2 endpoint is not configured, so the app is using heuristic fallback scoring
Fix:
- set
IFM_API_URL - set
IFM_API_KEYif your endpoint requires auth
Cause:
- you are not on a supported product-detail page
- the page is still loading dynamic content
- the scanner is turned off
Fix:
- turn the scanner on
- reload the retailer page
- wait a second for SPA content to settle
- test on a direct product page instead of a category page
Cause:
VITE_REWEAR_API_BASEis blank, or- the configured endpoint does not match the extension's expected route/response
Fix:
- use heuristic mode intentionally, or
- point the extension at a compatible live API
Cause:
- they expect a local Supabase CLI binary that is not checked into the repo
Fix:
- use the globally installed
supabasecommand directly, or - add
tools/supabase-cli/supabase.exeyourself
Because this is a hackathon project, there are a few things worth knowing up front:
- the naming is a little mixed between EcoThread, ReWear, and older prototype labels
- the extension's live API contract is not fully unified with the Supabase web app backend
- the repo contains experimental prototype paths in addition to the main demo path
- external AI/search services are optional but strongly affect the quality of the demo
None of that prevents the project from being run locally, but it helps to know which path is the "real" one:
- main web app path: React + Supabase
- main extension path: Chrome extension with heuristic mode or optional live API mode
- optional prototype path: Photon / tag-scanning experiment
If you are handing this off to a teammate before a demo, this is the shortest reliable checklist:
- install Node and Supabase CLI
- install root and extension dependencies
- create
/.env.local - create
/supabase/functions/.env.local - run
supabase start - run
supabase functions serve --env-file supabase/functions/.env.local --no-verify-jwt - run
npm run devfrom the repo root - run
npm run devfromextension/ - load
extension/dist/into Chrome - test one web-app flow and one extension flow before the actual demo
EcoThread was built for HackPrinceton 2026 as a fashion sustainability project that blends:
- personalized discovery
- browser-native product analysis
- sustainability scoring
- live retail aggregation
- hackathon-friendly fallbacks for resilience under demo conditions
If you are continuing work on this project after the hackathon, the best next step is to unify the extension and web-app scoring backends behind one clean API surface and add a root .env.example that mirrors the setup documented here.