Extract the rendered color palette from any website as a typed result.
Project description
colorsense
Extract the rendered color palette of any website as a structured, typed Python object.
Documentation: cassidyhhaas.github.io/colorsense
colorsense renders a page in a headless browser, harvests its design tokens and computed element colors, and classifies them by usage — what colors paint the page's surfaces, text, interactive elements, and borders — with ranked, scored entries per category, plus a derived 60/30/10 roles view (primary/secondary/accent/neutrals). The result is a frozen Pydantic model, ready for downstream consumers (including AI models) that need to understand a site's color identity.
import asyncio
from colorsense import Theme, UsageCategory, analyze
result = asyncio.run(analyze("https://example.com"))
surfaces = result.themes[Theme.light].usage.mapping[UsageCategory.surface]
print(surfaces[0].color.hex) # ranked entries; empty tuple when none detected
Installation
pip install colorsense
playwright install chromium
Requires Python 3.12+. Rendering uses headless Chromium via Playwright;
the browser binary is not a pip dependency, so run playwright install chromium once
after installing (on Linux, also playwright install-deps chromium for the OS libraries).
Quick start
analyze is async — await it from an event loop, or wrap it with asyncio.run:
import asyncio
from colorsense import analyze
result = asyncio.run(analyze("https://example.com"))
for theme, palette in result.themes.items():
for category, entries in palette.usage.mapping.items():
if entries: # every category is present; empty tuple when none detected
best = entries[0]
print(theme, category, best.color.hex, best.probability)
The usage view is the primary output: each category — surface, text,
interactive, border — maps to a probability-ranked tuple of entries; take [0] for
the best pick. palette.roles is a derived 60/30/10 interpretation (primary,
secondary, accent, neutral_light, neutral_dark) with a fit_score describing how
60/30/10-like the design is. Inside an async application (e.g. a FastAPI endpoint), just
result = await analyze(url).
See the usage guide for the full result schema, options, and fetch policy.
Command line
pip install colorsense also ships a colorsense command, so a first look needs no code:
colorsense https://example.com
colorsense https://example.com --dark --json > palette.json
The default output is a human-readable palette summary; --json emits the full
AnalysisResult schema. All flags are documented in the
usage guide.
Features
- Usage-keyed palette — what colors paint surfaces, text, interactive elements, and
borders, each entry carrying a confidence (
probability), page-area dominance (area), and the component types it came from (components). Preserves structure that a 60/30/10 taxonomy loses (e.g. a neutral design's gray text/border hierarchy). - Derived 60/30/10 roles view — five roles with ranked candidates, plus a
fit_scoredescribing how 60/30/10-like the design is. - Typed, serializable results —
analyzereturns a frozen PydanticAnalysisResult;result.model_dump_json()round-trips. - OKLCH out of the box — every
Colorcarries an sRGBhexplus cached OKLCH coordinates, so you can derive theme-matched tints, shades, and contrast without re-parsing hex strings. - Light and dark themes — opt into dark mode rendering; near-identical light/dark renders collapse to a single reported theme.
- Declared vs. rendered reconciliation — declared design-token intent is pooled into
the usage view, with per-theme
divergencereporting high-intent tokens declared but unused and prominent colors used but undeclared. Opt into the token list itself withanalyze(url, include_tokens=True). - Status-color filtering — success/error/warning tokens are detected and kept out of
the palette views, so an error banner never masquerades as a brand accent (they surface
in the opt-in token list with
semantic_role=status). - Polite, controllable fetching — configurable User-Agent,
robots.txtgate withCrawl-delaysupport, per-host rate limiting, render caching, and a per-request egress filter that gates every browser request and the policy's ownrobots.txtfetch. - Server-grade guard rails — a built-in private-network egress filter
(
block_private_networks) covering browser requests and the robots fetch alike, plus opt-in bounds on render concurrency (max_concurrent_renders) and total call time (max_total_seconds), and a browser launch-arg pass-through (browser_args) for e.g. capping the V8 heap per renderer. - Async-native and concurrent — themes render concurrently in one shared browser; CPU work is offloaded so the event loop stays responsive.
How it compares
Most palette tools quantize the pixels of an image or screenshot and return dominant colors with no semantics. colorsense renders the live page, reads computed styles and declared design tokens, and classifies colors by how they are used, with confidence scores — you get "this is the CTA/link color", not "this orange is common". If you just need dominant colors from an image, an image-quantization tool is simpler and the right choice.
Security
colorsense fetches and fully renders third-party pages. If untrusted or user-supplied URLs
can reach analyze from a server, treat it as an SSRF surface: validate hosts before
calling, and use PolitenessPolicy(request_filter=block_private_networks()) to gate every
request the rendered page makes — and the policy's own robots.txt fetch, including its
redirect hops; bound abuse with max_concurrent_renders and max_total_seconds. The
threat model and required controls are documented in
SECURITY.md — read it before exposing analyze to untrusted input.
Examples
The examples/ directory has two runnable starting points:
quickstart.py for trusted, hardcoded URLs, and
webservice/ — a FastAPI service that is a reference
implementation of the SECURITY.md controls for untrusted, user-supplied URLs.
Documentation
- Documentation site — everything below, plus the API reference, in one place.
- Usage guide — options, the result schema, errors, and fetch policy.
- Advanced guide — design-token auditing and custom tuning.
- SECURITY.md — threat model and consumer responsibilities.
- CONTRIBUTING.md — development setup and contribution workflow.
- CHANGELOG.md — release history.
Support & license
Bug reports and feature requests: GitHub Issues. Want to contribute? Start with CONTRIBUTING.md. Licensed under the MIT License.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file colorsense-0.5.0.tar.gz.
File metadata
- Download URL: colorsense-0.5.0.tar.gz
- Upload date:
- Size: 268.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0587d770b4e5d3f127ff8472aa6d4209d80746ecab0557014cc3abc8b0fb2499
|
|
| MD5 |
a88b7acfbea306b2a6987a2fa0004374
|
|
| BLAKE2b-256 |
9aa430c50393dce380053afb659d3f74b63c6d376fcc6b5e5418e8ce932be09b
|
Provenance
The following attestation bundles were made for colorsense-0.5.0.tar.gz:
Publisher:
publish.yml on cassidyhhaas/colorsense
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
colorsense-0.5.0.tar.gz -
Subject digest:
0587d770b4e5d3f127ff8472aa6d4209d80746ecab0557014cc3abc8b0fb2499 - Sigstore transparency entry: 1810777549
- Sigstore integration time:
-
Permalink:
cassidyhhaas/colorsense@d777619958ebfac240fd2aa900de58dce069f982 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/cassidyhhaas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d777619958ebfac240fd2aa900de58dce069f982 -
Trigger Event:
release
-
Statement type:
File details
Details for the file colorsense-0.5.0-py3-none-any.whl.
File metadata
- Download URL: colorsense-0.5.0-py3-none-any.whl
- Upload date:
- Size: 127.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9495dfd25c5635444837e5c1f9cd1293cd654431abbd32c4dc02ebba53c97c29
|
|
| MD5 |
fc6a4962c4b165d5022e84e0c0d8e4b2
|
|
| BLAKE2b-256 |
bc9adee295467ffc929906b6d902eb1e97f83983881fd4bbfdbfead5feb701ee
|
Provenance
The following attestation bundles were made for colorsense-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on cassidyhhaas/colorsense
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
colorsense-0.5.0-py3-none-any.whl -
Subject digest:
9495dfd25c5635444837e5c1f9cd1293cd654431abbd32c4dc02ebba53c97c29 - Sigstore transparency entry: 1810777579
- Sigstore integration time:
-
Permalink:
cassidyhhaas/colorsense@d777619958ebfac240fd2aa900de58dce069f982 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/cassidyhhaas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d777619958ebfac240fd2aa900de58dce069f982 -
Trigger Event:
release
-
Statement type: