add mixbox for javascript

This commit is contained in:
Ondrej Jamriska
2022-09-21 05:05:21 +02:00
parent ee38da6176
commit 4bfc462204
18 changed files with 3057 additions and 0 deletions

112
javascript/README.md Normal file
View File

@@ -0,0 +1,112 @@
# Mixbox for Javascript
```html
<script src="https://scrtwpns.com/mixbox.js"></script>
```
```javascript
import mixbox from 'https://scrtwpns.com/mixbox.esm.js'; // for ES6 module use this instead
```
### Node
```
npm install mixbox
```
```javascript
import mixbox from 'mixbox';
```
## Usage
```javascript
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);
console.log(mixed);
```
## Mixing Multiple Colors
```javascript
var z1 = mixbox.rgbToLatent(rgb1);
var z2 = mixbox.rgbToLatent(rgb2);
var z3 = mixbox.rgbToLatent(rgb3);
var zMix = new Array(mixbox.LATENT_SIZE);
for (var i = 0; i < zMix.length; i++) { // mix:
zMix[i] = (0.3*z1[i] + // 30% of rgb1
0.6*z2[i] + // 60% of rgb2
0.1*z3[i]); // 10% of rgb3
}
var rgbMix = mixbox.latentToRgb(zMix);
```
## Pigment Colors
| Pigment | | RGB | Float RGB | Linear RGB |
| --- | --- |:----:|:----:|:----:|
| Cadmium Yellow | <img src="https://scrtwpns.com/mixbox/pigments/cadmium_yellow.png"/> | 254, 236, 0 | 0.996, 0.925, 0.0 | 0.991, 0.839, 0.0 |
| Hansa Yellow | <img src="https://scrtwpns.com/mixbox/pigments/hansa_yellow.png"/> | 252, 211, 0 | 0.988, 0.827, 0.0 | 0.973, 0.651, 0.0 |
| Cadmium Orange | <img src="https://scrtwpns.com/mixbox/pigments/cadmium_orange.png"/> | 255, 105, 0 | 1.0, 0.412, 0.0 | 1.0, 0.141, 0.0 |
| Cadmium Red | <img src="https://scrtwpns.com/mixbox/pigments/cadmium_red.png"/> | 255, 39, 2 | 1.0, 0.153, 0.008 | 1.0, 0.02, 0.001 |
| Quinacridone Magenta | <img src="https://scrtwpns.com/mixbox/pigments/quinacridone_magenta.png"/> | 128, 2, 46 | 0.502, 0.008, 0.18 | 0.216, 0.001, 0.027 |
| Cobalt Violet | <img src="https://scrtwpns.com/mixbox/pigments/cobalt_violet.png"/> | 78, 0, 66 | 0.306, 0.0, 0.259 | 0.076, 0.0, 0.054 |
| Ultramarine Blue | <img src="https://scrtwpns.com/mixbox/pigments/ultramarine_blue.png"/> | 25, 0, 89 | 0.098, 0.0, 0.349 | 0.01, 0.0, 0.1 |
| Cobalt Blue | <img src="https://scrtwpns.com/mixbox/pigments/cobalt_blue.png"/> | 0, 33, 133 | 0.0, 0.129, 0.522 | 0.0, 0.015, 0.235 |
| Phthalo Blue | <img src="https://scrtwpns.com/mixbox/pigments/phthalo_blue.png"/> | 13, 27, 68 | 0.051, 0.106, 0.267 | 0.004, 0.011, 0.058 |
| Phthalo Green | <img src="https://scrtwpns.com/mixbox/pigments/phthalo_green.png"/> | 0, 60, 50 | 0.0, 0.235, 0.196 | 0.0, 0.045, 0.032 |
| Permanent Green | <img src="https://scrtwpns.com/mixbox/pigments/permanent_green.png"/> | 7, 109, 22 | 0.027, 0.427, 0.086 | 0.002, 0.153, 0.008 |
| Sap Green | <img src="https://scrtwpns.com/mixbox/pigments/sap_green.png"/> | 107, 148, 4 | 0.42, 0.58, 0.016 | 0.147, 0.296, 0.001 |
| Burnt Sienna | <img src="https://scrtwpns.com/mixbox/pigments/burnt_sienna.png"/> | 123, 72, 0 | 0.482, 0.282, 0.0 | 0.198, 0.065, 0.0 |
## Mixbox in WebGL
```javascript
var shader = `
precision highp float;
// uncomment the following line if you work in linear space
// #define MIXBOX_COLORSPACE_LINEAR
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());
```
```javascript
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 |
|:---:|:---:|:---:|
| <a href="https://scrtwpns.com/mixbox/examples/gradients.html"><img src="https://scrtwpns.com/mixbox/examples/gradients.png"/></a> | <a href="https://scrtwpns.com/mixbox/examples/mountains.html"><img src="https://scrtwpns.com/mixbox/examples/mountains.png"/></a> | <a href="https://scrtwpns.com/mixbox/examples/palette.html"><img src="https://scrtwpns.com/mixbox/examples/palette.png"/></a> |
| [source code](examples/gradients.js) | [source code](examples/mountains.js) | [source code](examples/palette.js) |
| Splash Art | Paint Mixer | Pigment Fluids |
|:---:|:---:|:---:|
| <a href="https://scrtwpns.com/mixbox/examples/splash.html"><img src="https://scrtwpns.com/mixbox/examples/splash.png"/></a> | <a href="https://scrtwpns.com/mixbox/examples/mixer.html"><img src="https://scrtwpns.com/mixbox/examples/mixer.png"/></a> | <a href="https://scrtwpns.com/mixbox/fluids"><img src="https://scrtwpns.com/mixbox/examples/fluids.png"/></a> |
| [source code](examples/splash.html) | [source code](examples/mixer.js) | [source code](https://scrtwpns.com/mixbox/fluids/script.js) |
## License
Copyright (c) 2022, Secret Weapons. All rights reserved.<br>
Mixbox is provided under the CC BY-NC 4.0 license for non-commercial use only.<br>
If you want to obtain commercial license, please contact: mixbox@scrtwpns.com

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="canvas" width="720" height="128"></canvas>
<script src="https://scrtwpns.com/mixbox.js"></script>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
var color1 = "rgb(0, 33, 133)"; // blue
var color2 = "rgb(252, 211, 0)"; // yellow
var n = canvas.width;
for (var i = 0; i < n; i++) {
context.fillStyle = mixbox.lerp(color1, color2, i / (n - 1));
context.fillRect(i, 0, 1, canvas.height);
}
</script>
</body>
</html>

View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body>
<!-- Bootstrap -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<!-- P5js -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script>
<!-- Mixbox -->
<script src="https://scrtwpns.com/mixbox.js"></script>
<!-- Code -->
<script src="gradients.js"></script>
<div class="container-fluid">
<div class="row justify-content-center align-items-center text-center py-5 my-3">
<div class="col-12"><main></main></div>
</div>
<div class="row justify-content-center align-items-center text-secondary text-center">
<div class="col-12">
<div class="d-inline" id="picker-A"></div>
<div class="d-inline" id="picker-B"></div>
<h3 class="pt-3">Pick colors</h3>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,147 @@
let colorPicker_A;
let colorPicker_B;
let color_A;
let color_B;
function setup() {
createCanvas(650, 465);
background(255);
colorMode(RGB);
colorPicker_A = createColorPicker('#002185');
colorPicker_A.parent('picker-A');
color_A = colorPicker_A.color();
colorPicker_B = createColorPicker('#fcd200');
colorPicker_B.parent('picker-B');
color_B = colorPicker_B.color();
drawGradient("Mixbox", color_A, color_B, 50, 200, 65, 465);
drawGradient("RGB", color_A, color_B, 250, 400, 65, 465);
drawGradient("OkLab", color_A, color_B, 450, 600, 65, 465);
}
function draw() {
if(color_A.toString() != colorPicker_A.color().toString() || color_B.toString() != colorPicker_B.color().toString())
{
background(255);
color_A = colorPicker_A.color();
color_B = colorPicker_B.color();
drawGradient("Mixbox", color_A, color_B, 50, 200, 65, 465);
drawGradient("RGB", color_A, color_B, 250, 400, 65, 465);
drawGradient("OkLab", color_A, color_B, 450, 600, 65, 465);
}
}
function drawGradient(method, color1, color2, x1, x2, y1, y2)
{
textSize(28);
textStyle(BOLD);
fill(79, 118, 123);
text(method, x1 + (x2-x1)/2 - textWidth(method)/2, y1-30);
let mixedColor;
for (let y = y1; y <= y2; y++)
{
let t = (y-y1)/(y2-y1);
if(match(method, 'Mixbox'))
{
mixedColor = mixbox.lerp(color1, color2, t);
}
else if(match(method, 'RGB'))
{
mixedColor = lerpColor(color1, color2, t);
}
else if(match(method, 'OkLab'))
{
let c1 = [red(color1), green(color1), blue(color1)];
let c2 = [red(color2), green(color2), blue(color2)];
let tmp = linear_to_rgb (oklab_to_linear_srgb(linearMix(linear_srgb_to_oklab(rgb_to_linear(c1)),linear_srgb_to_oklab(rgb_to_linear(c2)),t)));
mixedColor = color(tmp[0], tmp[1], tmp[2]);
}
strokeWeight(2);
stroke(mixedColor);
line(x1, y, x2, y);
noStroke();
}
}
/* THE FOLLOWING CODE IS HANDLING THE CONVERSION TO OkLAB SPACE */
/* https://bottosson.github.io/posts/oklab/ */
function linear_srgb_to_oklab(c)
{
let l = 0.4122214708 * c[0] + 0.5363325363 * c[1] + 0.0514459929 * c[2];
let m = 0.2119034982 * c[0] + 0.6806995451 * c[1] + 0.1073969566 * c[2];
let s = 0.0883024619 * c[0] + 0.2817188376 * c[1] + 0.6299787005 * c[2];
let l_ = Math.cbrt(l);
let m_ = Math.cbrt(m);
let s_ = Math.cbrt(s);
var lab = [ 0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_,
1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_,
0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_ ];
return lab;
}
function oklab_to_linear_srgb(c)
{
let l_ = c[0] + 0.3963377774 * c[1] + 0.2158037573 * c[2];
let m_ = c[0] - 0.1055613458 * c[1] - 0.0638541728 * c[2];
let s_ = c[0] - 0.0894841775 * c[1] - 1.2914855480 * c[2];
let l = l_*l_*l_;
let m = m_*m_*m_;
let s = s_*s_*s_;
var lrgb = [ 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s];
return lrgb;
}
function rgb_to_linear(rgb) // receiving Color object, returning array of 3 linear RGB values in range 0-1
{
var res = [0,0,0];
var float_rgb = [rgb[0]/255, rgb[1]/255, rgb[2]/255];
for (let i = 0; i < 3; ++i)
{
let c = float_rgb[i];
if (c >= 0.04045)
res[i] = pow((c + 0.055)/(1 + 0.055), 2.4);
else
res[i] = c / 12.92;
}
return res;
}
function linear_to_rgb(lrgb) // receiving array of 3 linear RGB values, returning an array of gamma encoded RGB values in range 0-255
{
var res = [0,0,0];
for (let i = 0; i < 3; ++i)
{
let c = lrgb[i];
if (c >= 0.0031308)
res[i] = 1.055 * pow(c, 1.0/2.4) - 0.055;
else
res[i] = 12.92 * c;
}
return [round(res[0]*255), round(res[1]*255), round(res[2]*255)];
}
function linearMix (a, b, t)
{
var res = [0,0,0];
for(let i=0; i<3; i++)
{
res[i] = a[i] * (1-t) + b[i]*t;
}
return res;
}

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="blue" class="box">BLUE</div>
<div id="mixed" class="box">MIXED</div>
<div id="yellow" class="box">YELLOW</div>
<script src="https://scrtwpns.com/mixbox.js"></script>
<script>
var blue = "rgb(0, 33, 133)";
var yellow = "rgb(252, 211, 0)";
var t = 0.5;
var mixed = mixbox.lerp(blue, yellow, t);
document.getElementById("blue").style.backgroundColor = blue;
document.getElementById("mixed").style.backgroundColor = mixed;
document.getElementById("yellow").style.backgroundColor = yellow;
</script>
<style>
.box{width: 200px; height: 200px; padding: 10px; margin: 10px; color: white; font-weight: bold;}
</style>
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<!-- P5js -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script>
<!-- Mixbox -->
<script src="https://scrtwpns.com/mixbox.js"></script>
<!-- Code -->
<script src="mixer.js"></script>
<div align="center" style="margin-top: 50px;">
<main> </main>
</div>
</body>
</html>

View File

@@ -0,0 +1,133 @@
let width = 650;
let height = 650;
let center_x = width/2;
let center_y = height/2;
let outer_radius = 300;
let inner_radius = 100;
let circle_radius = 45;
var colors = [];
var centers_outside = [];
var centers_inside = [];
var sliders_pos = [];
var mix_t = [];
let numPigments = 0;
let step = 0;
let dragged = -1;
function setup() {
createCanvas(650, 650);
background(255);
colorMode(RGB);
stroke(125);
strokeWeight(3);
colors = [color( 255,236,4), color( 252,211,0), color( 255,105,0), color( 225,35,1), color( 191,0,18), color( 128,2,46), color( 78,1,66), color( 74,0,101), color( 16,31,61), color( 13, 27, 68), color( 25, 0, 89), color( 8,34,138), color( 12, 69,118), color( 6, 54, 51), color( 0,74,41), color( 84,50,36), color( 58,39,0), color( 13,9,1), color(249,250,249)];
numPigments = colors.length;
step = TWO_PI / numPigments;
for(let i=0; i<numPigments; i++)
{
let x0 = center_x + sin(i * step) * inner_radius;
let x1 = center_x + sin(i * step) * outer_radius;
let y0 = center_y + cos(i * step) * inner_radius;
let y1 = center_y + cos(i * step) * outer_radius;
centers_inside.push( createVector(x0, y0));
centers_outside.push(createVector(x1, y1));
mix_t.push(0);
sliders_pos.push(createVector(x1,y1));
fill(colors[i]);
line(x0, y0, x1, y1);
ellipse(x1, y1, circle_radius, circle_radius);
fill(200);
ellipse(center_x, center_y, inner_radius*2, inner_radius*2);
}
}
function draw() {
if(dragged > -1)
{
mix_t[dragged] = get_t(centers_outside[dragged].x, centers_outside[dragged].y, centers_inside[dragged].x, centers_inside[dragged].y, mouseX, mouseY);
sliders_pos[dragged] = createVector(centers_outside[dragged].x - sin(dragged * step) * mix_t[dragged] * (outer_radius-inner_radius),
centers_outside[dragged].y - cos(dragged * step) * mix_t[dragged] * (outer_radius-inner_radius));
background(255);
let weights = 0;
for(let i=0; i<numPigments; i++)
{
line(centers_inside[i].x, centers_inside[i].y, centers_outside[i].x, centers_outside[i].y);
fill(colors[i]);
ellipse(sliders_pos[i].x, sliders_pos[i].y, circle_radius, circle_radius);
weights += mix_t[i];
}
if(weights > 0.000001)
{
let latent_mix = [0,0,0,0,0,0,0];
for(let j=0; j<numPigments; j++)
{
if(mix_t[j]>0.000001)
{
let latent = mixbox.rgbToLatent(colors[j]);
let t = mix_t[j]/weights;
for(let k=0; k<latent.length; k++)
{
latent_mix[k] += latent[k] * t;
}
}
}
let mixed_color = mixbox.latentToRgb(latent_mix);
fill(mixed_color);
ellipse(center_x, center_y, inner_radius*2, inner_radius*2);
}
else
{
fill(200);
ellipse(center_x, center_y, inner_radius*2, inner_radius*2);
}
}
}
function mousePressed()
{
for(let i=0; i<numPigments; i++)
{
if(mouseX > sliders_pos[i].x - circle_radius/2 &&
mouseX < sliders_pos[i].x + circle_radius/2 &&
mouseY > sliders_pos[i].y - circle_radius/2 &&
mouseY < sliders_pos[i].y + circle_radius/2)
{
dragged = i;
}
}
}
function mouseReleased()
{
dragged = -1;
}
function get_t (ax, ay, bx, by, qx, qy)
{
let u = createVector(bx-ax, by-ay);
let v = createVector(qx-ax, qy-ay);
let d = (u.x*v.x + u.y*v.y) / u.mag();
let t = d/u.mag();
return clamp(t, 0.0, 1.0);
}
function clamp(x, lowerlimit, upperlimit) {
if (x<lowerlimit) {return lowerlimit;}
else if(x>upperlimit){return upperlimit;}
else {return x;}
}

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<!-- P5js -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script>
<!-- Mixbox -->
<script src="https://scrtwpns.com/mixbox.js"></script>
<!-- Code -->
<script src="mountains.js"></script>
<div align="center" style="margin-top: 50px;">
<main></main>
</div>
</body>
</html>

View File

@@ -0,0 +1,90 @@
let magenta, yellow, phthalo_blue, titanium_white, phthalo_medium;
function setup()
{
createCanvas(800, 650);
background(80);
colorMode(RGB);
strokeWeight(2);
magenta = color(128,2,46);
yellow = color(255,236,4);
phthalo_blue = color(13,27,68);
titanium_white = color(249,251,249);
phthalo_medium = color(mixbox.lerp(phthalo_blue, titanium_white, 0.5));
}
function draw()
{
for(let y=0; y<height; y++ )
{
let t = y*0.8/(height/2.3);
let mix_col = mixbox.lerp(magenta, yellow,t);
stroke(mix_col);
line(0, y, width, y);
noStroke();
}
draw_mountain(460, 350, width/4, height);
draw_mountain(430, 550, width/4*3, height);
draw_mountain(370, 200, width/2-100, height);
draw_mountain(250, 250, width/4*2.7, height);
draw_sun(width/2, height/4, 100);
updatePixels();
noLoop();
}
function draw_mountain (mount_height, mount_width, base_x, base_y)
{
let xoff1 = random(0, 300);
let xoff2 = random(0, 300);
let xoff3 = random(0, 300);
let nScl1 = 0.009;
let nScl2 = 0.009;
let mountain_curr_width = 2;
for(let y=0; y<mount_height; y++)
{
let middle = noise(xoff1) * mountain_curr_width;
let x1 = -mountain_curr_width - noise(xoff2)*mountain_curr_width/2;
let x2 = mountain_curr_width + noise(xoff3)*mountain_curr_width/2;
for(let x=x1; x<middle; x++)
{
let t = (x -x1) / (middle - x1);
let gradient_color = mixbox.lerp(phthalo_blue, phthalo_medium, t);
gradient_color = mixbox.lerp(gradient_color, titanium_white, (1-y/mount_height)/1.5);
set(base_x + x, (base_y-mount_height) + y, color(gradient_color));
}
for(let x=middle; x<x2; x++)
{
let t = pow((x-middle)/(x2-middle), 1.5);
let gradient_color = mixbox.lerp(titanium_white, phthalo_medium, t);
gradient_color = mixbox.lerp(gradient_color, titanium_white, (1-y/mount_height)/1.5);
set(base_x + x, (base_y-mount_height) + y, color(gradient_color));
}
mountain_curr_width += mount_width/mount_height * (exp(y/mount_height * 1.5)-0.3);
xoff1 += nScl1;
xoff2 += nScl2;
xoff3 += nScl2;
}
}
function draw_sun(center_x, center_y, radius)
{
for (let y = center_y-radius/2; y < center_y+radius/2; y++)
{
for (let x = center_x-radius/2; x < center_x+radius/2; x++)
{
let d = dist(x,y,center_x,center_y);
if (d < radius/2)
{
let sun_color = mixbox.lerp(titanium_white, get(x,y), 0.5);
sun_color = mixbox.lerp(get(x,y), sun_color, (y-(center_y-radius/2))/radius); // vertical fade
set(x, y, color(sun_color));
}
}
}
}

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script>
<script src="https://scrtwpns.com/mixbox.js"></script>
<script>
function setup() {
createCanvas(400, 400);
}
function draw() {
var color1 = color(0, 33, 133); // blue
var color2 = color(252, 211, 0); // yellow
var colorMix = mixbox.lerp(color1, color2, 0.5);
background(colorMix);
}
</script>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<!-- P5js -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script>
<!-- Mixbox -->
<script src="https://scrtwpns.com/mixbox.js"></script>
<!-- Code -->
<script src="palette.js"></script>
<div align="center" style="margin-top: 50px;">
<main></main>
</div>
</body>
</html>

View File

@@ -0,0 +1,129 @@
var visited = []; // coordinates of the boxes the mouse has visited while pressed once
var boxes = []; // all boxes that should be displayed and colored, item is an array [x, y, R, G, B]
let boxSize = 40;
let color1;
let color2;
let valid_start = false;
let valid_end = false;
function setup() {
createCanvas(800, 680);
background(80);
colorMode(RGB);
rectMode(CENTER);
boxes.push([ 60, 60,[ 13, 27, 68]]); // phthalo blue
boxes.push([580, 180,[255, 236, 4]]); // bis yellow
boxes.push([420, 60,[255, 236, 4]]); // bis yellow
boxes.push([220, 300,[255, 208, 0]]); // hansa yellow
boxes.push([420, 380,[ 25, 0, 89]]); // ultramarine blue
boxes.push([500, 260,[ 25, 0, 89]]); // ultramarine blue
boxes.push([700, 380,[225, 35, 1]]); // cadmium red
boxes.push([580, 580,[128, 2, 46]]); // magenta
boxes.push([100, 580,[249, 250, 249]]); // white
boxes.push([260, 580,[249, 250, 249]]); // white
drawBoxes();
}
function draw()
{
// record visited boxes
if(mouseIsPressed === true)
{
let x = snapToGrid(mouseX);
let y = snapToGrid(mouseY);
let alreadyIn = false;
for(let v=0; v<visited.length; v++)
{
if(visited[v][0]==x && visited[v][1]==y) {alreadyIn = true};
}
if(!alreadyIn) {visited.push([x,y]);}
}
// draw overlay on visited boxes
if (visited.length > 0)
{
stroke(230);
noFill();
setLineDash([5, 5]);
for(let v=0; v<visited.length; v++)
{
rect(visited[v][0], visited[v][1], boxSize, boxSize);
}
}
}
function mousePressed()
{
// sample color from canvas
[color1, valid_start] = sampleColor(snapToGrid(mouseX), snapToGrid(mouseY));
}
function mouseReleased()
{
// sample color from canvas
[color2, valid_end] = sampleColor(snapToGrid(mouseX), snapToGrid(mouseY));
// calculate color of visited boxes & push them into boxes
if(mouseinside())
{
if(valid_start && valid_end)
{
let numVisited = visited.length;
for(let v=0; v<numVisited; v++)
{
let t = numVisited>1 ? v * 1.0/(numVisited-1) : 1;
let mixedColor = mixbox.lerp(color1, color2, t);
boxes.push([visited[v][0], visited[v][1], mixedColor]);
}
}
else{alert("You must start and end inside colored squares.");}
}
// clear visited array
visited = [];
// redraw screen to erase the overlay, clear background, draw boxes
background(80);
drawBoxes();
}
function drawBoxes()
{
noStroke();
for(let b=0; b<boxes.length; b++)
{
fill(color(boxes[b][2]));
rect(boxes[b][0], boxes[b][1], boxSize, boxSize);
}
}
function sampleColor (x, y)
{
for(let b=0; b<boxes.length; b++)
{
if(boxes[b][0] == x && boxes[b][1] == y)
{
return [color(boxes[b][2]), true];
}
}
return [color(255), false];
}
function snapToGrid (x){
return boxSize/2 + floor(x/boxSize) * boxSize;
}
function setLineDash(list) {
drawingContext.setLineDash(list);
}
function mouseinside(){
if(mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {return true;}
else {return false};
}

View File

@@ -0,0 +1,162 @@
<!--
Inspired by Processing sketch by Okazz
https://openprocessing.org/sketch/1533000
-->
<!DOCTYPE html>
<html>
<head>
</head>
<body style="background-color: #dee0e3;">
<!-- Mixbox -->
<script src="https://scrtwpns.com/mixbox.js"></script>
<div id="cnvs" align="center" style="margin-top: 50px;"> </div>
<!-- Code -->
<script>
var width = 800;
var height = 650;
var numSplashes = 270;
var frame = 0;
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
document.getElementById("cnvs").appendChild(canvas);
var ctx = canvas.getContext("2d");
var pixels = new Uint8ClampedArray(width*height*4);
var imageData = new ImageData(pixels,width,height);
fillBackground(255,255,255, 240, 240, 230, 255);
// grid for perlin noise
var grid = [];
var nodes = 128;
var noiseScale = 0.007;
for (let i=0; i<nodes*nodes; i++)
{
var unit = [];
var angle = Math.random() * 2 * Math.PI;
unit.push(Math.cos(angle));
unit.push(Math.sin(angle));
grid.push(unit);
}
// color palette
var colors = [ [249,250,249], [249,250,249], [255,236,4], [126,2,46], [255,236,4], [13,27,68], [255,236,4], [249,250,249], [249,250,249]];
function update()
{
frame++;
if (frame < numSplashes)
{
window.requestAnimationFrame(update);
}
};
function splash()
{
var cx = randomGauss(0.5,0.15)*width;
var cy = randomGauss(0.5,0.15)*height;
var radius = width/2;
for(var i=0; i<4; i++){radius = random(10,radius);}
numStains = randomInt(3, radius);
for(var i=0; i<numStains; i++)
{
var angle = random(0.0, 6.28);
var stain_x = cx + radius*Math.cos(angle);
var stain_y = cy + radius*Math.sin(angle);
var r = randomGauss(20, 15) * random(0,random(0,random(0,2)));
circle(stain_x, stain_y, r);
}
}
function circle(cx,cy,radius)
{
var colorIndex = Math.round((perlin(cx*noiseScale, cy*noiseScale)+1.0)*0.5*8);
var clr = colors[colorIndex];
var x0 = Math.round(clamp(cx-radius, 0, width));
var x1 = Math.round(clamp(cx+radius, 0, width));
var y0 = Math.round(clamp(cy-radius, 0, height));
var y1 = Math.round(clamp(cy+radius, 0, height));
var alpha = random(0.01,0.7);
for(var y=y0; y<y1; y++)
for(var x=x0; x<x1; x++)
{
var d = dist2D(cx, cy, x, y);
if( d < radius)
{
var r = pixels[(x+y*width)*4+0];
var g = pixels[(x+y*width)*4+1];
var b = pixels[(x+y*width)*4+2];
var bgColor = [r,g,b];
var mixedColor = mixbox.lerp(bgColor, clr, alpha * smoothStep(radius, radius*0.95, d));
pixels[(x+y*width)*4+0] = mixedColor[0];
pixels[(x+y*width)*4+1] = mixedColor[1];
pixels[(x+y*width)*4+2] = mixedColor[2];
pixels[(x+y*width)*4+3] = 255;
}
}
ctx.putImageData(imageData,0,0);
}
function fillBackground(r,g,b, r2, g2, b2, a)
{
for(var y=0;y<height;y++)
for(var x=0;x<width;x++)
{
pixels[(x+y*width)*4+0] = (x>40 && x<(width-40) && y>40 && y<(height-40)) ? r2 : r;
pixels[(x+y*width)*4+1] = (x>40 && x<(width-40) && y>40 && y<(height-40)) ? g2 : g;
pixels[(x+y*width)*4+2] = (x>40 && x<(width-40) && y>40 && y<(height-40)) ? b2 : b;
pixels[(x+y*width)*4+3] = a;
}
ctx.putImageData(imageData,0,0);
}
function clamp(x, lowerlimit, upperlimit) { if (x<lowerlimit) {return lowerlimit;} else if(x>upperlimit){return upperlimit;} else {return x;}}
function smoothStep(edge0, edge1, x) { x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); return x * x * (3 - 2 * x);}
function dist2D(ax,ay,bx,by) { return Math.sqrt((ax-bx)*(ax-bx) + (ay-by)*(ay-by));}
function box_muller(){ return Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random());}
function remap(x, from1, to1, from2, to2) { return from2 + (to2 - from2) * ((x-from1)/(to1-from1));}
function random(min, max) {return remap(Math.random(), 0.0, 1.0, min, max);}
function randomInt(min, max) {return Math.round(random(min, max));}
function randomGauss(mean, variation){return box_muller()*variation + mean;}
function randomGaussInt(mean, variation){return Math.round(box_muller()*variation + mean);}
function perlin(_x,_y)
{
var x = _x;
var y = _y;
var x0 = Math.floor(x);
var x1 = x0+1;
var y0 = Math.floor(y);
var y1 = y0+1;
var dot_LT = dot([x-x0,y-y0], grid[(x0&127) + (y0&127)*nodes]);
var dot_RT = dot([x-x1,y-y0], grid[(x1&127) + (y0&127)*nodes]);
var dot_LB = dot([x-x0,y-y1], grid[(x0&127) + (y1&127)*nodes]);
var dot_RB = dot([x-x1,y-y1], grid[(x1&127) + (y1&127)*nodes]);
var top = lerp_smooth(dot_LT, dot_RT, x-x0);
var bottom = lerp_smooth(dot_LB, dot_RB, x-x0);
var intensity = lerp_smooth (top, bottom, y-y0);
return intensity;
}
function dot(a,b) {return a[0] * b[0] + a[1] * b[1];}
function lerp_smooth(a,b,t){var t_smooth = 6*t**5 - 15*t**4 + 10*t**3; return a*(1-t_smooth) + b*t_smooth;}
for(var s=0; s<numSplashes; s++){splash();}
//update();
</script>
</body>
</html>

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<svg width="256" height="256">
<rect id="rect" width="256" height="256">
</svg>
<script src="https://scrtwpns.com/mixbox.js"></script>
<script>
var color1 = "rgb(0, 33, 133)"; // blue
var color2 = "rgb(252, 211, 0)"; // yellow
document.getElementById('rect').setAttribute('fill', mixbox.lerp(color1, color2, 0.5));
</script>
</body>
</html>

View File

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.js"></script>
<script src="https://scrtwpns.com/mixbox.js"></script>
<script>
var scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
scene.background = new THREE.Color(0xffffff);
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry = new THREE.BoxGeometry();
var cubes = [];
var color1 = new THREE.Color(0x002185); // blue
var color2 = new THREE.Color(0xfcd300); // yellow
for(var i = 0; i < 7; i++) {
var colorMix = mixbox.lerp(color1, color2, i / 6.0).toString();
const material = new THREE.MeshBasicMaterial( { color: colorMix } );
const cube = new THREE.Mesh( geometry, material );
cube.position.x = ((i / 6.0) * 2.0 - 1.0) * 4.5;
cube.rotation.x = i / 3.0;
scene.add(cube);
cubes.push(cube);
}
camera.position.z = 5;
function animate() {
requestAnimationFrame( animate );
for(var i=0;i<7;i++) {
cubes[i].rotation.x += 0.01;
cubes[i].rotation.y += 0.01;
}
renderer.render( scene, camera );
}
animate();
</script>
</body>
</html>

View File

@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="canvas" width="512" height="512"></canvas>
<script src="https://scrtwpns.com/mixbox.js"></script>
<script>
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
var vertexShader = `
attribute vec2 position;
void main(void) {
gl_Position = vec4(position, 0.0, 1.0);
}
`;
var fragmentShader = `
precision highp float;
uniform sampler2D mixbox_lut;
#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 = gl_FragCoord.x / 512.0; // mixing ratio
vec3 rgb = mixbox_lerp(rgb1, rgb2, t);
gl_FragColor = vec4(rgb, 1.0);
}
`;
fragmentShader = fragmentShader.replace('#include "mixbox.glsl"', mixbox.glsl());
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, compileShader(gl, gl.VERTEX_SHADER, vertexShader));
gl.attachShader(shaderProgram, compileShader(gl, gl.FRAGMENT_SHADER, fragmentShader));
gl.linkProgram(shaderProgram);
var vertexPosition = gl.getAttribLocation(shaderProgram, "position");
var positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
function render(now) {
gl.useProgram(shaderProgram);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, mixbox.lutTexture(gl));
gl.uniform1i(gl.getUniformLocation(shaderProgram, "mixbox_lut"), 0);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(vertexPosition, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(vertexPosition);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function compileShader(gl, type, source) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}
</script>
</body>
</html>

996
javascript/mixbox.esm.js Normal file

File diff suppressed because one or more lines are too long

1000
javascript/mixbox.js Normal file

File diff suppressed because one or more lines are too long