CODEX - Colour improvements

This commit is contained in:
2026-02-23 14:11:03 +02:00
parent 04b2a0bcb8
commit cf9fa91238
3 changed files with 101 additions and 9 deletions

View File

@@ -18,6 +18,39 @@ function parseStepsString(stepsStr) {
return String(stepsStr).trim().split(",").map((s) => parseBellNotation(s)); return String(stepsStr).trim().split(",").map((s) => parseBellNotation(s));
} }
function interpolateHue(t) {
const stops = [
[0.0, 190],
[0.24, 140],
[0.5, 56],
[0.82, 30],
[1.0, 0],
];
for (let i = 0; i < stops.length - 1; i++) {
const [aPos, aHue] = stops[i];
const [bPos, bHue] = stops[i + 1];
if (t >= aPos && t <= bPos) {
const local = (t - aPos) / (bPos - aPos || 1);
return aHue + (bHue - aHue) * local;
}
}
return stops[stops.length - 1][1];
}
function noteDotColor(noteNumber) {
const n = Number(noteNumber || 1);
const t = Math.min(1, Math.max(0, (n - 1) / 15));
const hue = interpolateHue(t);
return `hsl(${hue}, 78%, 68%)`;
}
function noteDotGlow(noteNumber) {
const n = Number(noteNumber || 1);
const t = Math.min(1, Math.max(0, (n - 1) / 15));
const hue = interpolateHue(t);
return `hsla(${hue}, 78%, 56%, 0.45)`;
}
function normalizeFileUrl(url) { function normalizeFileUrl(url) {
if (!url || typeof url !== "string") return null; if (!url || typeof url !== "string") return null;
if (url.startsWith("http") || url.startsWith("/api")) return url; if (url.startsWith("http") || url.startsWith("/api")) return url;
@@ -193,10 +226,10 @@ export default function BinaryTableModal({ open, melody, builtMelody, files, arc
width: "60%", width: "60%",
height: "60%", height: "60%",
borderRadius: "9999px", borderRadius: "9999px",
backgroundColor: "var(--btn-primary)", backgroundColor: noteDotColor(noteIdx + 1),
opacity: enabled ? 1 : 0, opacity: enabled ? 1 : 0,
transform: enabled ? "scale(1)" : "scale(0.4)", transform: enabled ? "scale(1)" : "scale(0.4)",
boxShadow: enabled ? "0 0 10px 3px rgba(116, 184, 22, 0.5)" : "none", boxShadow: enabled ? `0 0 10px 3px ${noteDotGlow(noteIdx + 1)}` : "none",
transition: "opacity 140ms ease, transform 140ms ease, box-shadow 180ms ease", transition: "opacity 140ms ease, transform 140ms ease, box-shadow 180ms ease",
}} }}
/> />
@@ -225,4 +258,3 @@ export default function BinaryTableModal({ open, melody, builtMelody, files, arc
</div> </div>
); );
} }

View File

@@ -22,6 +22,39 @@ function stepToHex(stepValue) {
return `0x${(stepValue >>> 0).toString(16).toUpperCase().padStart(4, "0")}`; return `0x${(stepValue >>> 0).toString(16).toUpperCase().padStart(4, "0")}`;
} }
function interpolateHue(t) {
const stops = [
[0.0, 190],
[0.24, 140],
[0.5, 56],
[0.82, 30],
[1.0, 0],
];
for (let i = 0; i < stops.length - 1; i++) {
const [aPos, aHue] = stops[i];
const [bPos, bHue] = stops[i + 1];
if (t >= aPos && t <= bPos) {
const local = (t - aPos) / (bPos - aPos || 1);
return aHue + (bHue - aHue) * local;
}
}
return stops[stops.length - 1][1];
}
function noteDotColor(noteNumber) {
const n = Number(noteNumber || 1);
const t = Math.min(1, Math.max(0, (n - 1) / 15));
const hue = interpolateHue(t);
return `hsl(${hue}, 78%, 68%)`;
}
function noteDotGlow(noteNumber) {
const n = Number(noteNumber || 1);
const t = Math.min(1, Math.max(0, (n - 1) / 15));
const hue = interpolateHue(t);
return `hsla(${hue}, 78%, 56%, 0.45)`;
}
function playStep(audioCtx, stepValue, noteDurationMs) { function playStep(audioCtx, stepValue, noteDurationMs) {
if (!audioCtx) return; if (!audioCtx) return;
@@ -527,10 +560,10 @@ export default function MelodyComposer() {
width: "54%", width: "54%",
height: "54%", height: "54%",
borderRadius: "9999px", borderRadius: "9999px",
backgroundColor: "var(--btn-primary)", backgroundColor: noteDotColor(noteIndex + 1),
opacity: enabled ? 1 : 0, opacity: enabled ? 1 : 0,
transform: enabled ? "scale(1)" : "scale(0.4)", transform: enabled ? "scale(1)" : "scale(0.4)",
boxShadow: enabled ? "0 0 10px 3px rgba(116, 184, 22, 0.5)" : "none", boxShadow: enabled ? `0 0 10px 3px ${noteDotGlow(noteIndex + 1)}` : "none",
transition: "opacity 140ms ease, transform 140ms ease, box-shadow 180ms ease", transition: "opacity 140ms ease, transform 140ms ease, box-shadow 180ms ease",
}} }}
/> />

View File

@@ -110,12 +110,39 @@ function applyNoteAssignments(rawStepValue, noteAssignments) {
return result; return result;
} }
function interpolateHue(t) {
const stops = [
[0.0, 190], // bright teal/blue
[0.24, 140], // green
[0.5, 56], // yellow
[0.82, 30], // orange
[1.0, 0], // red
];
for (let i = 0; i < stops.length - 1; i++) {
const [aPos, aHue] = stops[i];
const [bPos, bHue] = stops[i + 1];
if (t >= aPos && t <= bPos) {
const local = (t - aPos) / (bPos - aPos || 1);
return aHue + (bHue - aHue) * local;
}
}
return stops[stops.length - 1][1];
}
function bellDotColor(assignedBell) { function bellDotColor(assignedBell) {
const bell = Number(assignedBell || 0); const bell = Number(assignedBell || 0);
if (bell <= 0) return "rgba(148,163,184,0.7)"; if (bell <= 0) return "rgba(148,163,184,0.7)";
const t = Math.min(1, Math.max(0, (bell - 1) / 15)); const t = Math.min(1, Math.max(0, (bell - 1) / 15));
const light = 62 - t * 40; // bright green -> very dark green const hue = interpolateHue(t);
return `hsl(132, 74%, ${light}%)`; return `hsl(${hue}, 78%, 68%)`;
}
function bellDotGlow(assignedBell) {
const bell = Number(assignedBell || 0);
if (bell <= 0) return "rgba(100,116,139,0.35)";
const t = Math.min(1, Math.max(0, (bell - 1) / 15));
const hue = interpolateHue(t);
return `hsla(${hue}, 78%, 56%, 0.45)`;
} }
const mutedStyle = { color: "var(--text-muted)" }; const mutedStyle = { color: "var(--text-muted)" };
@@ -490,11 +517,11 @@ export default function PlaybackModal({ open, melody, builtMelody, files, archet
height: "68%", height: "68%",
borderRadius: "9999px", borderRadius: "9999px",
backgroundColor: isUnassigned ? "rgba(100,116,139,0.7)" : bellDotColor(assignedBell), backgroundColor: isUnassigned ? "rgba(100,116,139,0.7)" : bellDotColor(assignedBell),
color: "var(--text-white)", color: "#111827",
opacity: dotVisible ? 1 : 0, opacity: dotVisible ? 1 : 0,
transform: dotVisible ? "scale(1)" : "scale(0.4)", transform: dotVisible ? "scale(1)" : "scale(0.4)",
boxShadow: dotVisible boxShadow: dotVisible
? (isUnassigned ? "0 0 8px 2px rgba(100,116,139,0.35)" : `0 0 10px 3px ${bellDotColor(assignedBell)}66`) ? (isUnassigned ? "0 0 8px 2px rgba(100,116,139,0.35)" : `0 0 10px 3px ${bellDotGlow(assignedBell)}`)
: "none", : "none",
transition: "opacity 140ms ease, transform 140ms ease, box-shadow 180ms ease", transition: "opacity 140ms ease, transform 140ms ease, box-shadow 180ms ease",
}} }}