rybitten
Version:
A color space conversion library for transforming between RGB and RYB colors.
368 lines (263 loc) β’ 14.9 kB
Markdown
# RYBitten π΄π‘π΅
A color space conversion library for transforming between RGB and RYB (Red-Yellow-Blue) colors.
[](https://badge.fury.io/js/rybitten)
[](https://github.com/meodai/RYBitten/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/rybitten)
[](https://opensource.org/licenses/MIT)
<img src="screenshots/colorcloth.png" alt="Cloth particles using RYBitten" />
## What is RYBitten?
**RYBitten** is a lightweight library for translating colors between RGB and a custom RYB (Red-Yellow-Blue) color space. It's designed for developers, generative artists, and designers who want to create harmonious, consistent, or randomized color palettes effortlessly. The library emulates Johannes Itten's chromatic circle using trilinear interpolation and customizable options, making it a versatile tool for creative projects.
> "Play becomes joy, joy becomes work, work becomes play."
> **Johannes Itten, Bauhaus**
## What's new in 1.0 π
- **Stable API.** v1.0 locks the public surface; see [`STABILITY.md`](./STABILITY.md) for what's covered by semver.
- **Fully tested.** Vitest suite covering conversion math, HSL helpers, and an algorithmic roundtrip for every historical cube β 151 tests, 95%+ branch coverage.
- **Modern tooling.** ESLint 9 flat config, typescript-eslint 8, TypeScript 5.9.
- **CI-backed.** GitHub Actions runs lint, type-check, tests, build, and a tarball smoke test on Node 22 and the current LTS.
- **Tighter p5 types.** The p5 extension ships with real types instead of a file-wide `any` suppression.
## Features β¨
- **Color Conversion**: Effortlessly translate between RGB and a custom RYB space.
- **Easy Integration**: A tiny library with no dependencies.
- **Customizable Gamuts**: Experiment with historical color spaces or create your own.
- **Subtractive Color Model**: Emulates subtractive color.
## Installation π¦
Install RYBitten with your favorite package manager:
```bash
npm install rybitten
```
Or include it directly in your HTML:
```html
<script type="module">
import { ryb2rgb, rybHsl2rgb } from "https://esm.sh/rybitten/";
import { cubes } from "https://esm.sh/rybitten/cubes"; // Optional gamut presets
</script>
```
### Include RYBitten in your project
```javascript
// ES6 style
import { ryb2rgb } from 'rybitten';
// CommonJS style
const { ryb2rgb } = require('rybitten');
```
### Usage in Browser / p5.js (UMD)
For projects that don't use a bundler (like p5.js sketches), you can use the UMD build. This exposes a global `rybitten` object.
```html
<script src="https://unpkg.com/rybitten"></script>
<script>
function setup() {
createCanvas(400, 400);
noLoop();
}
function draw() {
// Access functions via the global 'rybitten' object
const rgb = rybitten.ryb2rgb([0, 1, 0]);
background(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
}
</script>
```
#### p5.js Color Mode Extension
RYBitten includes a special p5.js extension that adds native RYB color mode support, just like RGB, HSB, or HSL. The extension automatically initializes when you include the p5-specific UMD build:
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.js"></script>
<script src="https://unpkg.com/rybitten/dist/p5.rybitten.umd.js"></script>
<script>
function setup() {
createCanvas(400, 400);
// Use RYB color mode just like RGB or HSL!
colorMode(RYB);
background(255, 0, 0); // Red in RYB space
fill(0, 255, 0); // Yellow in RYB space
circle(200, 200, 100);
}
</script>
```
**Available Color Modes:**
- `RYB` - Red-Yellow-Blue color mode (0-255 range by default)
- `RYBHSL` - RYB with Hue-Saturation-Lightness (360, 100, 100 range by default)
**Using Custom Color Cubes in p5.js:**
```javascript
function setup() {
createCanvas(400, 400);
// Use a custom historical color cube
// Note: The UMD build does not export the cubes collection.
// Copy the cube values you want to use directly in your code.
colorMode(rybhsl([
[233/255, 199/255, 173/255], // White
[214/255, 76/255, 127/255], // Red
[238/255, 204/255, 124/255], // Yellow
[230/255, 174/255, 115/255], // Orange
[86/255, 141/255, 146/255], // Blue
[118/255, 83/255, 97/255], // Violet
[196/255, 192/255, 118/255], // Green
[60/255, 52/255, 40/255] // Black
]));
fill(0, 100, 50); // Hue 0, full saturation, mid lightness
rect(0, 0, 200, 200);
}
```
The extension works with both p5.js 1.x and 2.0, and supports both global and instance modes.
## Quick Start π
**All RGB and RYB values are in the range `[0, 1]`.**
```javascript
import { ryb2rgb } from 'rybitten';
const rgbColor = ryb2rgb([1, 0, 0.5]);
console.log(rgbColor); // Outputs the RGB equivalent
```
## API Reference π
### ryb2rgb(coords: ColorCoords, {cube?: ColorCube = RYB_ITTEN, easingFn? = easingSmoothstep}): ColorCoords
Convert RYB to RGB using trilinear interpolation.
- `coords`: `[0β¦1, 0β¦1, 0β¦1]` RYB coordinates
- `options`: (*optional*) An object with the following properties:
- `cube`: (*optional*): [See the note on the color cube below](#interpolation-color-cube-%EF%B8%8F) defaults to `RYB_ITTEN`
- `easingFn`: (*optional*) Custom easing function used for the interpolation, defaults to `smoothstep`
- `@return`: `[0β¦1, 0β¦1, 0β¦1]` RGB coordinates
**Note**: RYB uses a subtractive color model where black = all colors, white = no colors. **white will turn to black, and black will turn to white.**
### rybHsl2rgb(hsl: [h: number, s: number, l: number], options?): ColorCoords
Convert HSL to RGB, then apply the RYB space.
- `hsl`: Array of `[hue (0β¦360), saturation (0β¦1), lightness (0β¦1)]`
- `options`: (*optional*) An object with the following properties:
- `cube`: (*optional*) [See the note on the color cube below](#interpolation-color-cube-%EF%B8%8F)
- `easingFn`: (*optional*) A custom easing function for the interpolation, defaults to `smoothstep`
- `invertLightness`: (*optional*) Inverts the lightness value, defaults to `true` (0 is black, 1 is white), if set to `false` l:0 is white, l:1 is black
- `@return`: `[0β¦1, 0β¦1, 0β¦1]` RGB coordinates
Converts HSL coordinates to RGB, then translates them to the custom RYB color space using ryb2rgb. The HSL coordinates are in the range `[0,360], [0, 1], [0, 1]`. Lightness is inverted to match the RYB color space.
## Interpolation Color Cube ποΈ
The default RYB color cube used for interpolation in `RYBitten` is tuned to mimic Johannes Itten's chromatic circle. By adjusting the cube, you can achieve different effects and customize the RYB to RGB conversion.
The cube is inverted to match the subtractive color model, where white is the absence of color and black is the presence of all colors.
```javascript
const RYB_CUBE = [
// White
[253 / 255, 246 / 255, 237 / 255],
// Red
[227 / 255, 36 / 255, 33 / 255],
// Yellow
[243 / 255, 230 / 255, 0],
// Orange
[240 / 255, 142 / 255, 28 / 255],
// Blue
[22 / 255, 153 / 255, 218 / 255],
// Violet
[120 / 255, 34 / 255, 170 / 255],
// Green
[0, 142 / 255, 91 / 255],
// Black
[29 / 255, 28 / 255, 28 / 255],
];
```
## Custom Gamuts Presets π§
The library ships with a curated list of color gamuts that you can use to experiment with different color spaces. The default gamut is based on the work of Johannes Itten. But you can access other gamuts by importing the `CUBES` map.
Each gamut is an object with the following properties:
- `title`: The name of the color space
- `reference`: A reference image used to pick the edges of the custom color gamut
- `year`: The year the color space was introduced
- `cube`: The color cube used for interpolation
```javascript
import { rybHsl2rgb } from 'rybitten';
import { cubes } from 'rybitten/cubes';
const { cube } = cubes.get('munsell');
console.log(cube);
/**
* [
* ...8 ColorCoords entries
* ]
*/
rybHsl2rgb([0, 1, 0.5], { cube });
```
## TypeScript Support π
RYBitten is written in TypeScript and includes type definitions out of the box:
```typescript
import type { ColorCoords, ColorCube } from 'rybitten/cubes';
```
### Available Color Gamuts
The library provides a collection of historical and modern color gamuts through the `cubes` Map. Import and use them like this:
```javascript
import { rybHsl2rgb } from 'rybitten';
import { cubes } from 'rybitten/cubes';
// Access any gamut by its key
const munsellCube = cubes.get('munsell').cube;
const albersCube = cubes.get('albers').cube;
// Use it in color conversion
const rgbColor = rybHsl2rgb([0, 1, 0.5], { cube: munsellCube });
// Get metadata about the color space
const { title, author, year, reference } = cubes.get('munsell');
```
#### Historical Color Spaces
| Key | Title | Year | Reference |
| --- | --- | --- | --- |
| itten | Johannes Itten: Chromatic Circle | 1961 | [reference](references/farbkreis_extended.png) |
| itten-normalized | Johannes Itten: Chromatic Circle (Paper-white) | 1961 | [reference](references/Johannes-Itten-The-chromatic-circle-some-exercises-on-the-contrast-of-pure-colors.webp) |
| itten-neutral | Nathan Gossett & Baoquan Chen: Paint Inspired Color Compositing | 2004 | [reference](references/itten-ryb.pdf) |
| bezold | Wilhelm von Bezold: Farbentafel | 1874 | [reference](references/Bezold_Farbentafel_1874.jpg) |
| boutet | Claude Boutet: Twelve-color color circles | 1708 | [reference](references/Boutet_1708_color_circles.jpg) |
| hett | J. A. H. Hett: RGV Color Wheel | 1908 | [reference](references/RGV_color_wheel_1908.png) |
| schiffermueller | Ignaz SchiffermΓΌller: Versuch eines Farbensystems | 1772 | [reference](references/020_schiffermueller1.jpg) |
| harris | Moses Harris: The Natural System of Colours | 1766 | [reference](references/Moses_Harris_The_Natural_System_of_Colours.jpg) |
| harrisc82 | Moses Harris / C82: The Natural System of Colours | 1766 | [reference](references/harrisc82.png) |
| harrisc82alt | Moses Harris / C82: The Natural System of Colours (alt) | 1766 | [reference](references/harrisc82alt.png) |
| goethe | Goethe: Farbenkreis | 1809 | [reference](references/Goethe_Farbenkreis_zur_Symbolisierung_des_menschlichen_Geistes-_und_Seelenlebens_1809.jpg) |
| munsell | Munsell Color System | 1905 | [reference](references/munsell-atlas-11.jpg) |
| munsell-alt | Cleland & Munsell: A Grammar of Color | 1921 | [reference](references/munsell-alt.jpg) |
| hayer | Charles Hayter: New Practical Treatise on the Three Primitive Colours | 1826 | [reference](references/Color_diagram_Charles_Hayter.jpg) |
| bormann | Heinrich-Siegfried Bormann: Gouache tint study for Josef Alber's Preliminary Course" | 1931 | [reference](references/bormann.png) |
| chevreul | Michel Eugène Chevreul: Chromatic Circle | 1839 | |
| runge | Philipp Otto Runge: Farbenkugel | 1810 | [reference](references/farbenkugel.png) |
| maycock | Mark M. Maycock's "Scale of Normal Colors and their Hues" | 1895 | |
| colorprinter | John Earhart's "The Color Printer" | 1892 | |
| japschool | Japanese School Textbook | 1930 | [reference](references/japschool.png) |
| kindergarten1890 | Milton Bradley's Kindergarten Occupation Material | 1890 | [reference](references/kindergarten1890.jpg) |
| albers | Josef Albers: Interaction of Color | 1942 | [reference](references/albers-color-harmony.jpg) |
| lohse | Richard Paul Lohse's "Kunsthalle Bern Poster" | 1970 | [reference](references/lohse.png) |
| marvel-news | Marvel Comics: 64 Color Chart on Newsprint | 1982 | [reference](references/marvel-news.png) |
| apple80s | Apple: HyperCard User Manual | 1989 | [reference](references/apple80s.png) |
| apple90s | Apple: Macintosh Reference Manual | 1990 | [reference](references/apple90s.png) |
| rgb | James Clerk Maxwell's "Inverted RGB" | 1860 | |
#### Featured Artist Spectrum
The following color spaces were provided by artists and designers who have contributed to the project.
| Key | Title | Year | Reference |
| --- | --- | --- | --- |
| [ippsketch](https://ippsketch.com/) | Ippsketch: imposter syndrome | 2022 | [reference](references/ippsketch.png) |
| ten | Roni Kaufman's "Ten" | 2022 | [reference](references/ten.png) |
| pixelart | Tofu: Pixel Art | 2024 | [reference](references/pixelart.png) |
| ryan | Ryan: Compositions Palette | 2024 | [reference](references/ryan.png) |
| clayton | Greg Clayton: Intrinsic Value Plate | 2017 | [reference](references/A260P03_IntrinsicValue1.gif) |
## Utility Functions π οΈ
The library exports several utility functions that are used internally for color interpolation.
But if you are anything like me, you might find them useful for other purposes when working with this library.
### lerp(a: number, b: number, t: number): number
Linear interpolation between two values.
```javascript
import { lerp } from 'rybitten';
lerp(0, 100, 0.5); // returns 50
```
### blerp(a00: number, a01: number, a10: number, a11: number, tx: number, ty: number): number
Bilinear interpolation between four points in a 2D space. Useful for interpolating values on a rectangular grid.
```javascript
import { blerp } from 'rybitten';
// Interpolate between four corners of a unit square
blerp(0, 1, 1, 2, 0.5, 0.5); // returns center value
```
### trilerp(a000: number, a010: number, a100: number, a110: number, a001: number, a011: number, a101: number, a111: number, tx: number, ty: number, tz: number): number
Trilinear interpolation between eight points in a 3D space. This is the core interpolation function used for the color cube conversion.
```javascript
import { trilerp } from 'rybitten';
// Interpolate within a color cube
const value = trilerp(
0, 1, 1, 1, // front face values
0, 1, 1, 1, // back face values
0.5, 0.5, 0.5 // position in cube
);
```
### hslToRgb(hsl: [h: number, s: number, l: number]): ColorCoords
Converts standard HSL color values to RGB. Adapted from [Culori](https://github.com/Evercoder/culori) (MIT License).
- `hsl`: Array of `[hue (0β¦360), saturation (0β¦1), lightness (0β¦1)]`
- `@return`: `[0β¦1, 0β¦1, 0β¦1]` RGB coordinates
```javascript
import { hslToRgb } from 'rybitten';
hslToRgb([0, 1, 0.5]); // returns [1, 0, 0] (red)
hslToRgb([120, 1, 0.5]); // returns [0, 1, 0] (green)
hslToRgb([240, 1, 0.5]); // returns [0, 0, 1] (blue)
```
## Stability π
RYBitten follows semantic versioning from v1.0 onward. See [`STABILITY.md`](./STABILITY.md) for the exact contract: what's covered, what's explicitly not (notably, cube numeric values may be refined in minor releases), and the deprecation policy.
## License π
RYBitten is distributed under the [MIT License](./LICENSE).