RGB to XYZ converter
Get CIE 1931 XYZ values from an sRGB color: orange 255, 128, 0 gives X 48.96429558, Y 36.70460115 and Z 4.50627447 on the 0 to 100 scale. A solid starting point for Lab conversions and profile comparisons. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Resistance (Ω)
- 255
- G
- 128
- B
- 0
- X
- 48.96429558
- Y
- 36.70460115
- Z
- 4.50627447
- White point
- D65
This RGB to XYZ converter transforms red, green, and blue sRGB channel values into CIE 1931 XYZ tristimulus coordinates. It validates each channel, removes the nonlinear sRGB transfer curve, and applies the standard D65 conversion matrix for the 2-degree observer. The returned X, Y, and Z values use the familiar 0–100 scale, making the result ready for color science calculations, profile comparisons, and conversions into spaces such as CIE L*a*b*.
Enter a valid sRGB color
Provide the red, green, and blue channels as integers from 0 through 255. These are ordinary sRGB channel values, like those used by CSS rgb() colors and most screen-oriented image data. All three fields are required because an XYZ result describes the combined light contribution of the complete color.
The converter rejects missing channels, decimal values, nonnumeric values, and numbers outside the permitted range instead of silently clipping or guessing. That strict behavior is useful in automated pipelines: invalid source data produces a clear input error rather than a plausible-looking but incorrect coordinate. For example, pure red is entered as r 255, g 0, and b 0, while neutral middle gray uses the same number for all three channels.
The input is interpreted specifically as standard sRGB. Values from Adobe RGB, Display P3, ProPhoto RGB, camera-native RGB, or an unknown embedded profile require the matrix and transfer curve belonging to that space and should not be sent here as though they were sRGB.
Understand gamma linearization and the matrix
Stored sRGB values are nonlinear: they are encoded with a transfer curve designed for display systems and efficient visual precision. Matrix multiplication must operate on linear-light quantities, so the converter first divides each channel by 255 and applies the inverse sRGB companding function. Low channel values use the linear branch, while higher values use the 2.4-power branch defined for sRGB.
The resulting linear red, green, and blue components are multiplied by the standard 3 by 3 sRGB-to-XYZ matrix. That matrix expresses the sRGB primaries relative to the CIE 1931 2-degree standard observer and the D65 reference white. X broadly represents a red-weighted stimulus component, Y is aligned with relative luminance, and Z is weighted strongly toward blue, although none of the three should be treated as a simple renamed RGB channel.
Keeping gamma removal separate from matrix conversion is essential. Applying the matrix directly to encoded 0–255 values produces systematically wrong coordinates, particularly for midtones and dark colors.
Read and use the XYZ result
The response reports X, Y, and Z on a 0–100 scale, plus explicit white-point, observer, and scale labels so downstream code does not need to infer the convention. For the sRGB reference white, values are approximately X 95.047, Y 100, and Z 108.883; therefore Z can legitimately exceed 100 even though the scale label says 0–100. The label describes the conventional normalization where reference-white Y equals 100, not a hard clamp on every component.
XYZ is often an intermediate connection space. You can normalize the values and continue to CIE L*a*b*, calculate chromaticity coordinates, compare white points, or feed a color-management workflow that explicitly expects D65 XYZ data. When comparing results from another calculator or library, confirm its RGB profile, observer, reference white, output scaling, matrix coefficients, and rounding.
Differences in any of those conventions can produce different numbers without either implementation necessarily being defective.
What you can do with it
Prepare an sRGB color for LAB conversion
Use D65 XYZ as the connection step before applying the CIE L*a*b* reference-white transform.
Check a color library implementation
Compare deterministic matrix output for known sRGB samples while developing or testing color-processing code.
Calculate colorimetric coordinates
Turn web color channels into tristimulus values for luminance, chromaticity, and profile-aware analysis.
FAQ
What does the conversion cost?
The browser converter is free to run on this page.
Which RGB color space does this converter use?
It uses standard sRGB primaries and the sRGB inverse transfer curve. It does not interpret Adobe RGB, Display P3, or device-native RGB values.
Which white point and observer are used?
The matrix is referenced to the D65 white point and the CIE 1931 2-degree standard observer.
Why can Z be greater than 100?
The conventional scale normalizes reference-white Y to 100. Under D65, the reference-white Z coordinate is about 108.883, so values above 100 are valid.
Are decimal or out-of-range RGB channels accepted?
No. Every channel must be an integer from 0 through 255.
Related tools
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/rgb-xyz \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"r":255,"g":128,"b":0}'const res = await fetch("https://api.kit.forhosting.com/color/rgb-xyz", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"r": 255,
"g": 128,
"b": 0
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/color/rgb-xyz",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"r": 255,
"g": 128,
"b": 0
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/color/rgb-xyz", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"r":255,"g":128,"b":0}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"r":255,"g":128,"b":0}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/color/rgb-xyz", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"r": 255,
"g": 128,
"b": 0
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "color.rgb_xyz",
"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.
Limits
min_channel | 0 |
max_channel | 255 |
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. |
Technical notes
The same deterministic calculation runs in the browser and through the API.
Read and use the XYZ result
Results are rounded to eight decimal places to keep JSON stable while retaining more precision than most interface and reporting work needs.