# Grabient: cosine gradient palettes encoded as twelve numbers, searched by vector embedding

> Grabient is a web application at grabient.com that generates and searches color palettes stored as cosine gradients. It runs as a Cloudflare Worker with Hono and Solid.js islands, and its semantic search uses Cloudflare Vectorize with Workers AI embeddings.

**johnkorzhuk/grabient** — Cosine gradient generator & Palette finder

- Repository: https://github.com/johnkorzhuk/grabient
- Website: https://grabient.com
- Stars: 2,012 · Forks: 93
- Language: TypeScript
- License: NOASSERTION
- Published: 2026-10-11 · Updated: 2026-10-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/johnkorzhuk-grabient

## A cosine gradient palette fits in twelve numbers, and every palette URL encodes one completely

Grabient represents each palette as a cosine gradient: 4 coefficient vectors, each spanning 3 channels, that together reconstruct the full gradient from 12 numbers. The technique comes from Inigo Quilez, whose write-up is referenced in the acknowledgments. Because a palette is entirely described by those twelve numbers, its complete identity fits in the URL, and nothing needs to be looked up on the server to render it.

That compact representation has a practical consequence for the application design. A shared palette link is self-contained. Anyone who receives it can decode and render the gradient without a database query. The search and discovery features require the server, but any palette that has already been found can be shared and rendered without further server involvement.

thi-ng/cgg, also named in the acknowledgments, is another implementation of the cosine gradient technique. It is a library rather than a web application, making the comparison between building gradient math from a library and using a hosted tool that already embeds that math alongside a search interface.

## Semantic search embeds user queries and matches them against Vectorize palette vectors

Palette search in Grabient is vector-based rather than keyword-based. A user's query is embedded using Cloudflare Workers AI and matched against palette vectors stored in Cloudflare Vectorize. The README describes this as semantic matching, which means the matching works against meaning rather than against color names or tags.

Neither Vectorize nor Workers AI has a local emulator that the project considers useful, so both are marked remote in the Cloudflare configuration. Local development therefore always talks to the real staging Vectorize and Workers AI resources. That requirement distinguishes Grabient from most local dev setups: you cannot develop the search feature without a live Cloudflare account configured for staging, and the .dev.vars file must be filled in before pnpm dev will connect to those services.

## Setting up locally: three commands in sequence, with Cloudflare staging credentials required

Node 22 or newer is required, and wrangler will refuse to start on older versions. pnpm 10.14.0 is specified in the packageManager field of the root package.json. Setup starts with:

```bash
pnpm setup                      # install, then build packages/data-ops
cp apps/web/.dev.vars.example apps/web/.dev.vars   # then fill it in
pnpm dev                        # http://localhost:3001
```

pnpm setup runs pnpm i and then builds packages/data-ops, because the web app imports from that package's compiled dist/ rather than its source. Any subsequent change to packages/data-ops requires re-running pnpm build:data-ops before the web app reflects it.

To populate the local D1 database with sample palettes, start the dev server once so D1 initializes, then run:

```bash
pnpm db:seed
```

Local D1 uses a SQLite file in .wrangler/. KV, R2, Vectorize and Workers AI are all set to remote, pointing to real staging resources. Filling in .dev.vars from the example file is a prerequisite, not an optional step.

## Two packages: apps/web for the site, packages/data-ops for shared schema and gradient math

The workspace has 2 directories that matter to contributors. apps/web holds the site: a Cloudflare Worker running Hono for server-rendered HTML, with Solid.js islands added for interactive parts. packages/data-ops holds the shared database schema, queries, auth setup, gradient math and seed serialization. apps/web imports only the compiled output of packages/data-ops, which is why the setup step builds that package before starting the server.

The workspace uses pnpm filters directly. There is no turbo or nx configuration, and the README states this explicitly. Package relationships are expressed through pnpm's --filter flag in the root scripts, and that is the level of tooling available for cross-package builds.

The database is Cloudflare D1, accessed through Drizzle. Both the schema definition and the generated migration SQL files live under packages/data-ops/src/drizzle/. Generating a migration from a schema change requires running pnpm drizzle:generate from inside packages/data-ops. Applying migrations uses wrangler d1 migrations apply, with different database names for staging (grabient) and production (grabient-prod).

## Three environments, isolated D1 databases and a bare wrangler deploy that is intentionally broken

Three environments are defined in apps/web/wrangler.jsonc. Local development runs against a local SQLite file. Staging deploys to a worker named grabient-lite against a D1 database named grabient. Production deploys to grabient-production against grabient-prod.

Cloudflare does not inherit bindings between environments, so each environment declares its own bindings in wrangler.jsonc. grabient.com and cdn.grabient.com are attached to their workers in the Cloudflare dashboard rather than in the config file. No routes or custom_domain key appears in wrangler.jsonc, and the README notes that adding one would change live routing.

One explicit design decision guards against accidental deploys. There is no deployable top-level configuration, so a bare wrangler deploy resolves to an inert worker with no bindings and cannot reach either environment. The README says to always pass --env, with staging and production as the two valid values. Deploys are manual and there is no CI pipeline.

## Database migration and production seeding require separate commands per environment

Schema changes generate migrations through Drizzle. Running pnpm drizzle:generate from inside packages/data-ops produces the migration SQL from schema diffs. Applying those migrations uses separate commands per environment:

```bash
cd packages/data-ops
pnpm wrangler d1 migrations apply grabient --remote
pnpm wrangler d1 migrations apply grabient-prod --remote
```

The first applies to the staging D1 database named grabient; the second targets grabient-prod in production. Seeding also splits by environment: from packages/data-ops, staging uses pnpm db:seed remote --db=grabient and production uses pnpm db:seed:prod --db=grabient-prod. Local seeding from the repository root uses pnpm db:seed after pnpm dev has initialized the D1 file at least once.

Browsing the local database interactively is available through pnpm db:studio, which opens Drizzle Studio against the local SQLite file in .wrangler/.

## Testing commands, AI handoff documents in the root and the saas-kit base

2 verification commands cover the full project. pnpm test runs the test suites for data-ops, web and admin packages in sequence. pnpm typecheck runs type checking on both TypeScript projects in apps/web, using TypeScript at the version declared in the root devDependencies (^5.9.3). Deploys are manual with no CI pipeline.

The top-level repository directory includes CLAUDE.md, a .kimi-code/ directory, PASSOFF.md, PALETTE-NAMING-PASSOFF.md, SEO-PASSOFF.md and SEO-STRATEGY.md alongside the main code. PASSOFF.md and its two named variants indicate that palette naming and SEO each have their own handoff protocol between contributors and AI tools. The README does not document how these workflows operate. The project is built on top of saas-kit by Backpine as a base service template, with Grabient's gradient math and vector search added on top.

Grabient is under FSL-1.1-ALv2, which allows use and modification but prohibits building a competing commercial color palette service; the code converts to Apache 2.0 after two years, with full terms in LICENSE.md.

## Conclusion

Grabient is worth running locally if you need a reference implementation of cosine gradient palette generation on a Cloudflare Worker stack with semantic vector search. Before setup, verify your Cloudflare account has Vectorize, Workers AI, KV and R2 available in the staging environment, because local emulation for those services is disabled and the dev server always hits real staging resources. Read LICENSE.md before building on this code: FSL-1.1-ALv2 bars competing commercial palette services, and the Apache 2.0 conversion requires two years.

## FAQ

### What is Grabient?

Grabient is a web application at grabient.com that generates and searches color palettes. Palettes are stored as cosine gradients described by four 3-channel coefficient sets, and search is vector-based using Cloudflare Vectorize.

### How does Grabient store color palettes?

Each palette is stored as four 3-channel coefficients, which is twelve numbers in total. These reconstruct the full gradient using Inigo Quilez's cosine gradient technique, and the entire palette identity fits in the URL.

### What technology does Grabient run on?

Grabient runs as a Cloudflare Worker using Hono for server-rendered HTML and Solid.js islands for interactive parts. The database is Cloudflare D1 accessed through Drizzle, and semantic search uses Cloudflare Vectorize with Workers AI embeddings.

### Can I run Grabient locally without a Cloudflare account?

Local D1 runs as a SQLite file in .wrangler/ and does not require a remote database. Vectorize, Workers AI, KV and R2 are marked remote and always connect to real staging resources, so those features require a Cloudflare account with those services configured.

### What license is Grabient released under?

Grabient is released under the Functional Source License 1.1, Apache 2.0 Future License (FSL-1.1-ALv2). This license allows use and modification but prohibits building a competing commercial color palette service. After two years, the code converts to Apache 2.0.

## Sources

- [Issues](https://github.com/johnkorzhuk/grabient/issues)
- [johnkorzhuk/grabient on GitHub](https://github.com/johnkorzhuk/grabient)
- [Project website](https://grabient.com)
- [README](https://github.com/johnkorzhuk/grabient/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/johnkorzhuk-grabient
