Mixbox: Pigment-Based Color Mixing
Mixbox is a new blending method for natural color mixing. It produces saturated gradients with hue shifts and natural secondary colors during blending. Yellow and blue make green. The interface is simple - RGB in, RGB out. Internally, Mixbox treats colors as real-life pigments using the Kubelka & Munk theory to predict realistic color behavior. That way, colors act like actual paints and bring more vibrance and intuition into digital painting.
- Paper: https://scrtwpns.com/mixbox.pdf
- Video: https://youtu.be/ATzVPVNp1qA
- Talk: https://youtu.be/_qa5iWdfNKg
- Demo: https://scrtwpns.com/mixbox/painter
Mixbox is shipping in Rebelle 5 Pro as the Rebelle Pigments feature and in the Flip Fluids addon for Blender.
Usage
- C / C++:
#include "mixbox.h"and buildmixbox.cpptogether with your project - C#: use Mixbox package from NuGet
https://www.nuget.org/packages/Mixbox/2.0.0 - Java: add
implementation 'com.scrtwpns:mixbox:2.0.0'to your Gradle - JavaScript:
<script src="https://scrtwpns.com/mixbox.js"> - Node:
npm install mixbox - Python:
pip install pymixbox - Rust: add
mixbox = "2.0.0"to your Cargo.toml - Unity: add package from git url
git://github.com/scrtwpns/mixbox.git#upm - Godot: copy
godot\addonsto the root of your project - Shaders: load
mixbox_lut.pngas texture and includemixbox.glsl/.hlsl/.metalcode into your shader
Pigment Colors
C / C++
#include <stdio.h> #include "mixbox.h" int main() { unsigned char r1 = 0, g1 = 33, b1 = 133; // blue unsigned char r2 = 252, g2 = 211, b2 = 0; // yellow float t = 0.5; unsigned char r, g, b; mixbox_lerp(r1, g1, b1, // first color r2, g2, b2, // second color t, // mixing ratio &r, &g, &b); // result printf("%d %d %d\n", r, g, b); }
GLSL Shader
#ifdef GL_ES precision highp float; #endif uniform sampler2D mixbox_lut; // bind the "mixbox_lut.png" texture here #include "mixbox.glsl" // paste the contents of mixbox.glsl here void main(void) { vec3 rgb1 = vec3(0, 0.129, 0.522); // blue vec3 rgb2 = vec3(0.988, 0.827, 0); // yellow float t = 0.5; // mixing ratio vec3 rgb = mixbox_lerp(rgb1, rgb2, t); gl_FragColor = vec4(rgb, 1.0); }
Rust
fn main() { let rgb1 = [0, 33, 133]; // blue let rgb2 = [252, 211, 0]; // yellow let t = 0.5; // mixing ratio let [r, g, b] = mixbox::lerp(&rgb1, &rgb2, t); println!("{} {} {}", r, g, b); }
Python
import mixbox rgb1 = (0, 33, 133) # blue rgb2 = (252, 211, 0) # yellow t = 0.5 # mixing ratio rgb_mix = mixbox.lerp(rgb1, rgb2, t) print(rgb_mix)
JavaScript
<html> <body> <script src="https://scrtwpns.com/mixbox.js"></script> <script> var rgb1 = "rgb(0, 33, 133)"; // blue var rgb2 = "rgb(252, 211, 0)"; // yellow var t = 0.5; // mixing ratio var mixed = mixbox.lerp(rgb1, rgb2, t); document.body.style.background = mixed; </script> </body> </html>
Node
import mixbox from 'mixbox'; let rgb1 = "rgb(0, 33, 133)"; // blue let rgb2 = "rgb(252, 211, 0)"; // yellow let t = 0.5; // mixing ratio let mixed = mixbox.lerp(rgb1, rgb2, t); console.log(mixed);
Java
import java.awt.Color; import com.scrtwpns.Mixbox; class HelloMixbox { public static void main(String[] args) { Color color1 = new Color(0, 33, 133); // blue Color color2 = new Color(252, 211, 0); // yellow float t = 0.5f; // mixing ratio Color colorMix = new Color(Mixbox.lerp(color1.getRGB(), color2.getRGB(), t)); System.out.print(colorMix); } }
Android
package com.example.hellomixbox; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.graphics.Color; import com.scrtwpns.Mixbox; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); int color1 = Color.rgb(0, 33, 133); // blue int color2 = Color.rgb(252, 211, 0); // yellow float t = 0.5f; // mixing ratio int colorMix = Mixbox.lerp(color1, color2, t); View view = new View(this); view.setBackgroundColor(colorMix); setContentView(view); } }
C#
using System.Drawing; using Scrtwpns.Mixbox; public class HelloMixbox { public static void Main(string[] args) { Color color1 = Color.FromArgb(0, 33, 133); // blue Color color2 = Color.FromArgb(252, 211, 0); // yellow float t = 0.5f; // mixing ratio Color colorMix = Color.FromArgb(Mixbox.Lerp(color1.ToArgb(), color2.ToArgb(), t)); System.Console.WriteLine(colorMix); } }
Unity
using UnityEngine; using Scrtwpns.Mixbox; public class NewBehaviourScript : MonoBehaviour { void Start() { Color color1 = new Color(0.0f, 0.129f, 0.522f); // blue Color color2 = new Color(0.988f, 0.827f, 0.0f); // yellow float t = 0.5f; // mixing ratio Color colorMix = Mixbox.Lerp(color1, color2, t); Debug.Log(colorMix); } }
Unity Shader
Shader "MixboxHelloShader" { Properties { _MixboxLUT ("Mixbox LUT", 2D) = "white" {} // assign "Packages/Mixbox/Textures/MixboxLUT.png" _Color1 ("Color 1", Color) = (0, 0.129, 0.522, 1) // blue _Color2 ("Color 2", Color) = (0.988, 0.827, 0, 1) // yellow } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MixboxLUT; #include "Packages/com.scrtwpns.mixbox/ShaderLibrary/Mixbox.cginc" fixed4 _Color1; fixed4 _Color2; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 mixedColor = MixboxLerp(_Color1, _Color2, i.uv.x); return mixedColor; } ENDCG } } }
Unity Shader Graph
Godot
var Mixbox = preload("res://addons/mixbox/mixbox.gd") var color1 = Color(0.0, 0.129, 0.522) # blue var color2 = Color(0.988, 0.827, 0.0) # yellow var t = 0.5 # mixing ratio var color_mix = Mixbox.lerp(color1, color2, t) print(color_mix)
Godot Shader
shader_type canvas_item; uniform sampler2D mixbox_lut; // attach "addons/mixbox/mixbox_lut.png" here uniform vec4 color1 : hint_color = vec4(0.0, 0.129, 0.522, 1.0); // blue uniform vec4 color2 : hint_color = vec4(0.988, 0.827, 0.0, 1.0); // yellow #include "addons/mixbox/mixbox.gdshaderinc" void fragment() { COLOR = mixbox_lerp(color1, color2, UV.x); }
Godot VisualShader
WebGL
<script src="https://scrtwpns.com/mixbox.js"></script>
var shader = ` precision highp float; uniform sampler2D mixbox_lut; // bind mixbox.lutTexture(gl) here #include "mixbox.glsl" void main(void) { vec3 rgb1 = vec3(0, 0.129, 0.522); // blue vec3 rgb2 = vec3(0.988, 0.827, 0); // yellow float t = 0.5; // mixing ratio vec3 rgb = mixbox_lerp(rgb1, rgb2, t); gl_FragColor = vec4(rgb, 1.0); } `; shader = shader.replace('#include "mixbox.glsl"', mixbox.glsl());
gl.useProgram(shaderProgram); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, mixbox.lutTexture(gl)); gl.uniform1i(gl.getUniformLocation(shaderProgram, "mixbox_lut"), 0);
Examples
| Gradients | Mountains | Palette Snakes |
|---|---|---|
![]() |
![]() |
![]() |
| source code | source code | source code |
| Splash Art | Paint Mixer | Pigment Fluids |
|---|---|---|
![]() |
![]() |
![]() |
| source code | source code | source code |
Painter
This painting app runs two color mixing implementations in parallel: one based on Mixbox and the other that performs ordinary RGB mixing. The app allows switching between them on the fly, showing the differences between pigment-based mixing and the normal RGB mixing. To launch the painter in your browser, please click here.License
Copyright (c) 2022, Secret Weapons. All rights reserved.
Mixbox is provided under the CC BY-NC 4.0 license for non-commercial use only.
If you want to obtain commercial license, please contact: mixbox@scrtwpns.com






















