Skip to main content

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.

CI PyPI Python License: MIT

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 role — page, surface, banner, cta, action, text, link, border — across two complementary indexes: a color-keyed canonical index ("how each color is used") and a role-keyed projection ("which colors paint each role"). 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, UsageRole, analyze

result = asyncio.run(analyze("https://example.com"))
ctas = result.themes[Theme.light].usage.mapping[UsageRole.cta]
print(ctas[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():
    # "How is each color used?" — the canonical color-keyed index, ranked by prominence.
    for color in palette.colors:
        roles = ", ".join(f"{u.role}={u.weight:.2f}" for u in color.usages)
        print(theme, color.color.hex, roles)
    # "Which colors paint each role?" — the role-keyed projection.
    for role, entries in palette.usage.mapping.items():
        if entries:  # every role is present; empty tuple when none detected
            best = entries[0]
            print(theme, role, best.color.hex, best.probability)

Two complementary views describe usage. palette.colors is the canonical, color-keyed index: each measured ColorUsage carries its prominence ranking and the Usage roles it appears in (with a property_family rollup: background / text / border). palette.usage.mapping is the role-keyed projection: each UsageRolepage, surface, banner, cta, action, text, link, border — maps to a probability-ranked tuple of entries; take [0] for the best pick. 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

  • Color-keyed index — every measured color with the usage roles it appears in (and a background/text/border property_family rollup), ranked by an overall prominence. Answers "how is each color used?".
  • Role-keyed usage projection — what colors paint each usage role (page, surface, banner, cta, action, text, link, border), each entry carrying a confidence (probability), page-area dominance (area), and the component types it came from (components). Splitting CTA backgrounds from link text (and the page canvas from raised surfaces and chrome bars) preserves structure a 4-value taxonomy lost.
  • Typed, serializable resultsanalyze returns a frozen Pydantic AnalysisResult; result.model_dump_json() round-trips.
  • OKLCH out of the box — every Color carries an sRGB hex plus 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 divergence reporting high-intent tokens declared but unused and prominent colors used but undeclared. Opt into the token list itself with analyze(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.txt gate with Crawl-delay support, per-host rate limiting, render caching, and a per-request egress filter that gates every browser request and the policy's own robots.txt fetch.
  • 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.mdread 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

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

colorsense-0.6.0.tar.gz (279.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

colorsense-0.6.0-py3-none-any.whl (128.5 kB view details)

Uploaded Python 3

File details

Details for the file colorsense-0.6.0.tar.gz.

File metadata

  • Download URL: colorsense-0.6.0.tar.gz
  • Upload date:
  • Size: 279.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for colorsense-0.6.0.tar.gz
Algorithm Hash digest
SHA256 016b9ce0bbadcba5e28590efa4f18e82c8feebb99be76049de7d696be91b129a
MD5 b7709c4c93a41654016690a4b716f14e
BLAKE2b-256 5925973fff05322bef634ee145f9414af1bd377cc69dd19b0c886285a44f22b3

See more details on using hashes here.

Provenance

The following attestation bundles were made for colorsense-0.6.0.tar.gz:

Publisher: publish.yml on cassidyhhaas/colorsense

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file colorsense-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: colorsense-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 128.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for colorsense-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9ec51e7c7f59e8ca1f92bc1f6ee2c72b6a996d8ff6f7ca7c3cadea620895e06d
MD5 97124a1839d9315120c687c0e362de42
BLAKE2b-256 e818d2a4b136d0ae631a6f1074fb47400bbf9c176e49a72060de878297710189

See more details on using hashes here.

Provenance

The following attestation bundles were made for colorsense-0.6.0-py3-none-any.whl:

Publisher: publish.yml on cassidyhhaas/colorsense

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page