Spaces:
Running
Running
Deploy tonebox to the hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +4 -0
- tonebox/app.js +166 -0
- tonebox/gallery/lake/arctic.jpg +0 -0
- tonebox/gallery/lake/bleach-bypass.jpg +0 -0
- tonebox/gallery/lake/blockbuster.jpg +0 -0
- tonebox/gallery/lake/blue-hour.jpg +0 -0
- tonebox/gallery/lake/blue-tone.jpg +0 -0
- tonebox/gallery/lake/bw-green-filter.jpg +0 -0
- tonebox/gallery/lake/bw-high-contrast.jpg +0 -0
- tonebox/gallery/lake/bw-neutral.jpg +0 -0
- tonebox/gallery/lake/bw-red-filter.jpg +0 -0
- tonebox/gallery/lake/bw-soft.jpg +0 -0
- tonebox/gallery/lake/candlelight.jpg +0 -0
- tonebox/gallery/lake/cool-negative.jpg +0 -0
- tonebox/gallery/lake/cool-plus.jpg +0 -0
- tonebox/gallery/lake/cross-process.jpg +0 -0
- tonebox/gallery/lake/crush-blacks.jpg +0 -0
- tonebox/gallery/lake/cyanotype.jpg +0 -0
- tonebox/gallery/lake/day-for-night.jpg +0 -0
- tonebox/gallery/lake/desert-gold.jpg +0 -0
- tonebox/gallery/lake/dreamy-pastel.jpg +0 -0
- tonebox/gallery/lake/dungeon.jpg +0 -0
- tonebox/gallery/lake/dusk.jpg +0 -0
- tonebox/gallery/lake/expired-stock.jpg +0 -0
- tonebox/gallery/lake/faded-print.jpg +0 -0
- tonebox/gallery/lake/fog.jpg +0 -0
- tonebox/gallery/lake/frozen-tundra.jpg +0 -0
- tonebox/gallery/lake/golden-hour.jpg +0 -0
- tonebox/gallery/lake/horror-green.jpg +0 -0
- tonebox/gallery/lake/lava.jpg +0 -0
- tonebox/gallery/lake/lift-shadows.jpg +0 -0
- tonebox/gallery/lake/matte.jpg +0 -0
- tonebox/gallery/lake/midday-punch.jpg +0 -0
- tonebox/gallery/lake/moody-forest.jpg +0 -0
- tonebox/gallery/lake/moonlight.jpg +0 -0
- tonebox/gallery/lake/muted.jpg +0 -0
- tonebox/gallery/lake/neon.jpg +0 -0
- tonebox/gallery/lake/night-city.jpg +0 -0
- tonebox/gallery/lake/noir-blue.jpg +0 -0
- tonebox/gallery/lake/original.jpg +0 -0
- tonebox/gallery/lake/overcast.jpg +0 -0
- tonebox/gallery/lake/pastel.jpg +0 -0
- tonebox/gallery/lake/punchy.jpg +0 -0
- tonebox/gallery/lake/retro-8bit.jpg +0 -0
- tonebox/gallery/lake/scifi-clean.jpg +0 -0
- tonebox/gallery/lake/selenium.jpg +0 -0
- tonebox/gallery/lake/sepia-drama.jpg +0 -0
- tonebox/gallery/lake/sepia.jpg +0 -0
- tonebox/gallery/lake/silver-retention.jpg +0 -0
- tonebox/gallery/lake/slide-punch.jpg +0 -0
.gitattributes
CHANGED
|
@@ -47,3 +47,7 @@ groundcover/screenshot-1.png filter=lfs diff=lfs merge=lfs -text
|
|
| 47 |
groundcover/screenshot-2.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
groundcover/screenshot-3.png filter=lfs diff=lfs merge=lfs -text
|
| 49 |
groundcover/screenshot-4.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
groundcover/screenshot-2.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
groundcover/screenshot-3.png filter=lfs diff=lfs merge=lfs -text
|
| 49 |
groundcover/screenshot-4.png filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
tonebox/icon-512.png filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
tonebox/og.png filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
tonebox/samples/lake.jpg filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
tonebox/samples/market.jpg filter=lfs diff=lfs merge=lfs -text
|
tonebox/app.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Tonebox previewer. Photos and .cube files stay in the browser. © 2026 CyberMax. All rights reserved. */
|
| 2 |
+
(function () {
|
| 3 |
+
"use strict";
|
| 4 |
+
var CHECKOUT = "https://buy.polar.sh/polar_cl_MiDtlpQOhXytyuuOkWflnzvDFprMR1WRDE0LQ30bQPr"; // Polar checkout link (revenue/packs/polar/tonebox/polar.json)
|
| 5 |
+
var MAX_SIDE = 1600; // preview resolution; the download uses the full photo up to 6000 px
|
| 6 |
+
var $ = function (s) { return document.querySelector(s); };
|
| 7 |
+
var state = { looks: [], luts: {}, custom: null, current: "teal-orange", sample: "market", img: null, intensity: 1, fileName: "photo" };
|
| 8 |
+
var before = $("#before"), after = $("#after"), stage = $("#stage");
|
| 9 |
+
|
| 10 |
+
function toast(msg) { var t = $("#toast"); t.textContent = msg; t.classList.add("show"); clearTimeout(toast.t); toast.t = setTimeout(function () { t.classList.remove("show"); }, 2600); }
|
| 11 |
+
function warn(msg) { $("#warn").textContent = msg || ""; }
|
| 12 |
+
function lookById(id) { for (var i = 0; i < state.looks.length; i++) if (state.looks[i].id === id) return state.looks[i]; return null; }
|
| 13 |
+
function activeLut() { return state.current === "custom" ? state.custom : state.luts[state.current]; }
|
| 14 |
+
function activeName() { return state.current === "custom" ? (state.custom.title || "Your LUT") : (lookById(state.current) || {}).name; }
|
| 15 |
+
|
| 16 |
+
function drawScaled(img, canvas, maxSide) {
|
| 17 |
+
var s = Math.min(1, maxSide / Math.max(img.naturalWidth || img.width, img.naturalHeight || img.height));
|
| 18 |
+
canvas.width = Math.max(1, Math.round((img.naturalWidth || img.width) * s));
|
| 19 |
+
canvas.height = Math.max(1, Math.round((img.naturalHeight || img.height) * s));
|
| 20 |
+
var ctx = canvas.getContext("2d", { willReadFrequently: true });
|
| 21 |
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
| 22 |
+
return ctx;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
var base = null; // original pixels at preview size
|
| 26 |
+
function setImage(img) {
|
| 27 |
+
state.img = img;
|
| 28 |
+
var ctx = drawScaled(img, before, MAX_SIDE);
|
| 29 |
+
base = ctx.getImageData(0, 0, before.width, before.height);
|
| 30 |
+
after.width = before.width; after.height = before.height;
|
| 31 |
+
render();
|
| 32 |
+
}
|
| 33 |
+
var pending = false;
|
| 34 |
+
function render() {
|
| 35 |
+
if (!base || pending) return;
|
| 36 |
+
pending = true;
|
| 37 |
+
requestAnimationFrame(function () {
|
| 38 |
+
pending = false;
|
| 39 |
+
var lut = activeLut();
|
| 40 |
+
var out = new ImageData(new Uint8ClampedArray(base.data), base.width, base.height);
|
| 41 |
+
if (lut) ToneboxLUT.applyLut(out.data, lut, state.intensity);
|
| 42 |
+
after.getContext("2d").putImageData(out, 0, 0);
|
| 43 |
+
$("#tag-look").textContent = activeName() || "";
|
| 44 |
+
document.body.setAttribute("data-rendered", state.current + ":" + Math.round(state.intensity * 100));
|
| 45 |
+
});
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
function loadSample(name) {
|
| 49 |
+
state.sample = name; state.fileName = name;
|
| 50 |
+
var img = new Image();
|
| 51 |
+
img.onload = function () { setImage(img); };
|
| 52 |
+
img.src = "samples/" + name + ".jpg";
|
| 53 |
+
document.querySelectorAll("#samples .chip").forEach(function (b) { b.classList.toggle("on", b.getAttribute("data-sample") === name); });
|
| 54 |
+
document.querySelectorAll("#gallery img").forEach(function (im) { im.src = "gallery/" + name + "/" + im.getAttribute("data-id") + ".jpg"; });
|
| 55 |
+
document.querySelectorAll("#free-looks img").forEach(function (im) { im.src = "gallery/" + name + "/" + im.getAttribute("data-id") + ".jpg"; });
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
function openPhoto(file) {
|
| 59 |
+
if (!file || !/^image\//.test(file.type)) { warn("That file is not an image."); return; }
|
| 60 |
+
var url = URL.createObjectURL(file), img = new Image();
|
| 61 |
+
img.onload = function () { state.fileName = file.name.replace(/\.[^.]+$/, "") || "photo"; setImage(img); warn(""); document.querySelectorAll("#samples .chip").forEach(function (b) { b.classList.remove("on"); }); };
|
| 62 |
+
img.onerror = function () { warn("This browser can't open that image format. Try a JPG or PNG."); };
|
| 63 |
+
img.src = url;
|
| 64 |
+
}
|
| 65 |
+
function openCube(file) {
|
| 66 |
+
var r = new FileReader();
|
| 67 |
+
r.onload = function () {
|
| 68 |
+
try {
|
| 69 |
+
var lut = ToneboxLUT.parseCube(r.result);
|
| 70 |
+
lut.title = lut.title || file.name.replace(/\.cube$/i, "");
|
| 71 |
+
state.custom = lut; select("custom");
|
| 72 |
+
warn(""); toast("Loaded " + lut.title + " (" + lut.n + "-point)");
|
| 73 |
+
} catch (e) { warn("Couldn't read that LUT: " + e.message); }
|
| 74 |
+
};
|
| 75 |
+
r.readAsText(file);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
function select(id) {
|
| 79 |
+
state.current = id;
|
| 80 |
+
document.querySelectorAll("#free-looks .look").forEach(function (b) { b.classList.toggle("on", b.getAttribute("data-id") === id); });
|
| 81 |
+
render();
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
function save() {
|
| 85 |
+
if (!state.img) return;
|
| 86 |
+
var c = document.createElement("canvas"), ctx = drawScaled(state.img, c, 6000);
|
| 87 |
+
var id = ctx.getImageData(0, 0, c.width, c.height), lut = activeLut();
|
| 88 |
+
if (lut) ToneboxLUT.applyLut(id.data, lut, state.intensity);
|
| 89 |
+
ctx.putImageData(id, 0, 0);
|
| 90 |
+
c.toBlob(function (blob) {
|
| 91 |
+
var a = document.createElement("a");
|
| 92 |
+
a.href = URL.createObjectURL(blob);
|
| 93 |
+
a.download = state.fileName + "-" + (activeName() || "graded").toLowerCase().replace(/[^a-z0-9]+/g, "-") + ".jpg";
|
| 94 |
+
document.body.appendChild(a); a.click(); a.remove();
|
| 95 |
+
toast("Saved " + c.width + "×" + c.height + " JPG");
|
| 96 |
+
}, "image/jpeg", 0.92);
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
function buildUI(meta) {
|
| 100 |
+
state.looks = meta.looks;
|
| 101 |
+
var free = $("#free-looks");
|
| 102 |
+
meta.free.forEach(function (id) {
|
| 103 |
+
var l = lookById(id), b = document.createElement("button");
|
| 104 |
+
b.className = "look" + (id === state.current ? " on" : ""); b.setAttribute("data-id", id); b.type = "button";
|
| 105 |
+
b.innerHTML = '<img alt="" loading="lazy" data-id="' + id + '" src="gallery/market/' + id + '.jpg"><span></span>';
|
| 106 |
+
b.querySelector("span").textContent = l.name;
|
| 107 |
+
b.addEventListener("click", function () { select(id); });
|
| 108 |
+
free.appendChild(b);
|
| 109 |
+
});
|
| 110 |
+
var cols = ["All"].concat(meta.looks.map(function (l) { return l.collection; }).filter(function (c, i, a) { return a.indexOf(c) === i; }));
|
| 111 |
+
var filters = $("#filters");
|
| 112 |
+
cols.forEach(function (c, i) {
|
| 113 |
+
var b = document.createElement("button"); b.className = "chip" + (i ? "" : " on"); b.type = "button"; b.textContent = c;
|
| 114 |
+
b.addEventListener("click", function () {
|
| 115 |
+
filters.querySelectorAll(".chip").forEach(function (x) { x.classList.toggle("on", x === b); });
|
| 116 |
+
document.querySelectorAll("#gallery figure").forEach(function (f) { f.hidden = !(c === "All" || f.getAttribute("data-col") === c); });
|
| 117 |
+
});
|
| 118 |
+
filters.appendChild(b);
|
| 119 |
+
});
|
| 120 |
+
var g = $("#gallery");
|
| 121 |
+
meta.looks.forEach(function (l) {
|
| 122 |
+
var f = document.createElement("figure"); f.className = "g-item"; f.setAttribute("data-col", l.collection);
|
| 123 |
+
f.innerHTML = '<img alt="" loading="lazy" width="480" height="320" data-id="' + l.id + '" src="gallery/market/' + l.id + '.jpg"><figcaption><b></b><small></small></figcaption>';
|
| 124 |
+
f.querySelector("img").alt = l.name + " LUT preview";
|
| 125 |
+
f.querySelector("b").textContent = (l.free ? "🔓 " : "") + l.name;
|
| 126 |
+
f.querySelector("small").textContent = l.collection + " · " + l.desc;
|
| 127 |
+
if (l.free) { f.style.cursor = "pointer"; f.addEventListener("click", function () { select(l.id); window.scrollTo({ top: 0, behavior: "smooth" }); }); }
|
| 128 |
+
g.appendChild(f);
|
| 129 |
+
});
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
function wire() {
|
| 133 |
+
$("#samples").addEventListener("click", function (e) { var s = e.target.getAttribute("data-sample"); if (s) loadSample(s); });
|
| 134 |
+
$("#photo").addEventListener("change", function (e) { openPhoto(e.target.files[0]); e.target.value = ""; });
|
| 135 |
+
$("#cube").addEventListener("change", function (e) { if (e.target.files[0]) openCube(e.target.files[0]); e.target.value = ""; });
|
| 136 |
+
$("#intensity").addEventListener("input", function (e) { state.intensity = e.target.value / 100; $("#int-val").textContent = e.target.value + "%"; render(); });
|
| 137 |
+
$("#split").addEventListener("input", function (e) {
|
| 138 |
+
var v = e.target.value;
|
| 139 |
+
$("#before-clip").style.clipPath = "inset(0 " + (100 - v) + "% 0 0)";
|
| 140 |
+
$("#handle").style.left = v + "%";
|
| 141 |
+
});
|
| 142 |
+
$("#btn-save").addEventListener("click", save);
|
| 143 |
+
["dragenter", "dragover"].forEach(function (ev) { stage.addEventListener(ev, function (e) { e.preventDefault(); stage.classList.add("drag"); }); });
|
| 144 |
+
["dragleave", "drop"].forEach(function (ev) { stage.addEventListener(ev, function (e) { e.preventDefault(); stage.classList.remove("drag"); }); });
|
| 145 |
+
stage.addEventListener("drop", function (e) {
|
| 146 |
+
var f = e.dataTransfer.files[0]; if (!f) return;
|
| 147 |
+
if (/\.cube$/i.test(f.name)) openCube(f); else openPhoto(f);
|
| 148 |
+
});
|
| 149 |
+
var buy = $("#buy");
|
| 150 |
+
if (/^https:\/\//.test(CHECKOUT)) { buy.href = CHECKOUT; $("#top-buy").href = "#pack"; }
|
| 151 |
+
else { buy.textContent = "Coming soon"; buy.removeAttribute("href"); buy.classList.add("ghost"); }
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
if (/[?&]thanks=1/.test(location.search)) setTimeout(function () { toast("Thanks for your order! Your download link is in the Polar receipt email."); }, 800);
|
| 155 |
+
window.toneboxApp = { state: state, select: select, openCube: openCube, save: save };
|
| 156 |
+
wire();
|
| 157 |
+
Promise.all([
|
| 158 |
+
fetch("luts/looks.json").then(function (r) { return r.json(); }),
|
| 159 |
+
fetch("luts/preview.bin").then(function (r) { return r.arrayBuffer(); }),
|
| 160 |
+
]).then(function (res) {
|
| 161 |
+
var meta = res[0], luts = ToneboxLUT.parsePreviewBin(res[1]);
|
| 162 |
+
meta.free.forEach(function (id, i) { state.luts[id] = luts[i]; });
|
| 163 |
+
buildUI(meta);
|
| 164 |
+
loadSample("market");
|
| 165 |
+
}).catch(function (e) { warn("Couldn't load the looks: " + e.message); });
|
| 166 |
+
})();
|
tonebox/gallery/lake/arctic.jpg
ADDED
|
tonebox/gallery/lake/bleach-bypass.jpg
ADDED
|
tonebox/gallery/lake/blockbuster.jpg
ADDED
|
tonebox/gallery/lake/blue-hour.jpg
ADDED
|
tonebox/gallery/lake/blue-tone.jpg
ADDED
|
tonebox/gallery/lake/bw-green-filter.jpg
ADDED
|
tonebox/gallery/lake/bw-high-contrast.jpg
ADDED
|
tonebox/gallery/lake/bw-neutral.jpg
ADDED
|
tonebox/gallery/lake/bw-red-filter.jpg
ADDED
|
tonebox/gallery/lake/bw-soft.jpg
ADDED
|
tonebox/gallery/lake/candlelight.jpg
ADDED
|
tonebox/gallery/lake/cool-negative.jpg
ADDED
|
tonebox/gallery/lake/cool-plus.jpg
ADDED
|
tonebox/gallery/lake/cross-process.jpg
ADDED
|
tonebox/gallery/lake/crush-blacks.jpg
ADDED
|
tonebox/gallery/lake/cyanotype.jpg
ADDED
|
tonebox/gallery/lake/day-for-night.jpg
ADDED
|
tonebox/gallery/lake/desert-gold.jpg
ADDED
|
tonebox/gallery/lake/dreamy-pastel.jpg
ADDED
|
tonebox/gallery/lake/dungeon.jpg
ADDED
|
tonebox/gallery/lake/dusk.jpg
ADDED
|
tonebox/gallery/lake/expired-stock.jpg
ADDED
|
tonebox/gallery/lake/faded-print.jpg
ADDED
|
tonebox/gallery/lake/fog.jpg
ADDED
|
tonebox/gallery/lake/frozen-tundra.jpg
ADDED
|
tonebox/gallery/lake/golden-hour.jpg
ADDED
|
tonebox/gallery/lake/horror-green.jpg
ADDED
|
tonebox/gallery/lake/lava.jpg
ADDED
|
tonebox/gallery/lake/lift-shadows.jpg
ADDED
|
tonebox/gallery/lake/matte.jpg
ADDED
|
tonebox/gallery/lake/midday-punch.jpg
ADDED
|
tonebox/gallery/lake/moody-forest.jpg
ADDED
|
tonebox/gallery/lake/moonlight.jpg
ADDED
|
tonebox/gallery/lake/muted.jpg
ADDED
|
tonebox/gallery/lake/neon.jpg
ADDED
|
tonebox/gallery/lake/night-city.jpg
ADDED
|
tonebox/gallery/lake/noir-blue.jpg
ADDED
|
tonebox/gallery/lake/original.jpg
ADDED
|
tonebox/gallery/lake/overcast.jpg
ADDED
|
tonebox/gallery/lake/pastel.jpg
ADDED
|
tonebox/gallery/lake/punchy.jpg
ADDED
|
tonebox/gallery/lake/retro-8bit.jpg
ADDED
|
tonebox/gallery/lake/scifi-clean.jpg
ADDED
|
tonebox/gallery/lake/selenium.jpg
ADDED
|
tonebox/gallery/lake/sepia-drama.jpg
ADDED
|
tonebox/gallery/lake/sepia.jpg
ADDED
|
tonebox/gallery/lake/silver-retention.jpg
ADDED
|
tonebox/gallery/lake/slide-punch.jpg
ADDED
|