Generate a complementary color from any hex or RGB value
A complementary color sits directly opposite a base color on the hue wheel. This generator accepts a familiar hex or RGB value, preserves its saturation and lightness, rotates the hue by exactly 180 degrees, and returns a normalized base color alongside the generated complement.
Run — free
Use it to establish a strong two-color relationship for interfaces, illustrations, charts, brand explorations, or design tokens. The calculation is deterministic, so the same valid input always produces the same result, while malformed color strings are rejected with a clear input error.
Enter a precise base color
Start with the color you already know. The generator accepts three-digit hex such as #369, six-digit hex such as #336699, the same six digits without a hash, or an RGB function such as rgb(51, 102, 153). Hex letters are case-insensitive, and the returned value is normalized to uppercase six-digit hex so it can be copied into CSS, design tokens, documentation, or a palette file without further cleanup. RGB channels must be whole numbers from zero through 255. Formats that introduce ambiguity, including named CSS colors, alpha channels, percentages, incomplete hex strings, and channels outside the valid range, are rejected instead of being guessed. Strict validation is useful in automation because a typing mistake cannot silently become an unintended palette choice. Send the value in the color field; the value alias is also accepted for integrations that use a generic input name. The response repeats the normalized base color, making it easy to confirm exactly what the service interpreted before using the generated result.
Understand the 180-degree hue rotation
The calculation converts the base color from RGB into the HSL color model, adds 180 degrees to its hue, wraps the result around the 360-degree wheel, and converts it back to RGB. Saturation and lightness stay unchanged. This is a hue-wheel complement, not a simple inversion of every RGB channel, so it follows the relationship designers usually mean when they ask for an opposite color. Red moves toward cyan, green moves toward magenta, and blue moves toward yellow, while less obvious intermediate hues receive the same exact treatment. Rounding occurs only when the converted result returns to the integer RGB channels required by common digital color formats. Neutral grays have zero saturation and therefore no visually meaningful hue; rotating their stored hue leaves the neutral appearance unchanged, which is the mathematically consistent result. The response includes both hex and separate red, green, and blue channels for the base and complement. That structure supports visual use as well as programmatic pipelines without requiring callers to parse a formatted string.
Use the result as a starting relationship
Complementary colors create strong separation, but the raw pair is a starting point rather than a guarantee of accessible or attractive composition. Fully saturated complements placed in equal amounts can vibrate visually, so many interfaces use one hue as the dominant surface and reserve its complement for a small accent, status marker, data series, or call to action. You can also create tints and shades from either returned color while preserving the recognizable relationship between the two hue families. For text and controls, check contrast against the actual background because opposite positions on the hue wheel do not imply equal luminance or compliance with accessibility guidelines. The deterministic output is especially helpful when a build process needs to derive the same accent from a stored brand color on every run. It also works well in prototyping tools, chart configuration, theme generation, and asset pipelines where a compact JSON response is easier to consume than a visual color picker. API requests use the published base price of $0.002, while the browser experience can perform the same pure calculation locally.
What you can do with it
Create an interface accent
Generate an opposing hue for buttons, badges, highlights, or focus elements from an established theme color.
Build a two-series chart palette
Derive a clearly separated second hue from the first series color, then verify contrast in the final chart context.
Automate design-token generation
Turn a stored brand hex value into a deterministic complementary token during a build or theming workflow.
FAQ
What color formats are accepted?
Use three- or six-digit hex, six-digit hex without a hash, or rgb(r, g, b) with integer channels from 0 to 255.
How is the complementary color calculated?
The base RGB value is converted to HSL, its hue is rotated by 180 degrees, and the result is converted back to RGB.
Is this the same as inverting RGB channels?
No. The generator rotates hue while preserving HSL saturation and lightness; direct RGB inversion is a different operation.
What happens with gray, white, or black?
Neutral colors have no visible hue, so a hue rotation leaves their appearance unchanged.
What does an API request cost?
Each request uses the published base price of $0.002. The same deterministic calculation is available in the browser experience.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/color/complementary-generate \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"color":"#336699"}'const res = await fetch("https://api.kit.forhosting.com/color/complementary-generate", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"color": "#336699"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/color/complementary-generate",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"color": "#336699"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/color/complementary-generate", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"color":"#336699"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"color":"#336699"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/color/complementary-generate", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"color": "#336699"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "color.complementary_generate",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |