You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
_contrast = 1.2,
_saturation = 0.9,
_overallRedTint = 0.98,
_overallGreenTint = 1.01,
_overallBlueTint = 1.04,
_highlightRedBoost = 1.07,
_highlightGreenBoost = 1.05,
_highlightBlueReduction = 0.96,
_shadowGreenAdd = 3,
_shadowBlueAdd = 5,
_grainAmount = 5
) {
// Ensure parameters are numbers, provide defaults if parsing fails
const contrast = Number(_contrast) || 1.2;
const saturation = Number(_saturation) || 0.9;
const overallRedTint = Number(_overallRedTint) || 0.98;
const overallGreenTint = Number(_overallGreenTint) || 1.01;
const overallBlueTint = Number(_overallBlueTint) || 1.04;
const highlightRedBoost = Number(_highlightRedBoost) || 1.07;
const highlightGreenBoost = Number(_highlightGreenBoost) || 1.05;
const highlightBlueReduction = Number(_highlightBlueReduction) || 0.96;
const shadowGreenAdd = Number(_shadowGreenAdd) || 3;
const shadowBlueAdd = Number(_shadowBlueAdd) || 5;
const grainAmount = Number(_grainAmount) || 5;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const highlightThreshold = 170; // Pixels brighter than this are considered_highlights
const shadowThreshold = 70; // Pixels darker than this are considered shadows
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// 1. Overall Color Tint (emulating Cinestill 50D's daylight balance and character)
// Cinestill 50D is daylight balanced, tends to be cool with good blues.
r *= overallRedTint;
g *= overallGreenTint;
b *= overallBlueTint;
// Clamp after initial tint
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 2. Contrast Adjustment
// Formula: NewValue = (OldValue - 128) * Contrast + 128
r = (r - 128) * contrast + 128;
g = (g - 128) * contrast + 128;
b = (b - 128) * contrast + 128;
// Clamp after contrast
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 3. Saturation Adjustment
// Formula: NewValue = Luminance + Saturation * (OldValue - Luminance)
// Luminance calculation (standard weights)
const lum = 0.299 * r + 0.587 * g + 0.114 * b;
r = lum + saturation * (r - lum);
g = lum + saturation * (g - lum);
b = lum + saturation * (b - lum);
// Clamp after saturation
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 4. Selective Highlight and Shadow Adjustments
const intensity = (r + g + b) / 3; // Simple average intensity
if (intensity > highlightThreshold) { // Highlight adjustments for warmth
r *= highlightRedBoost;
g *= highlightGreenBoost; // Warms towards yellow/orange
b *= highlightBlueReduction; // Reducing blue enhances warmth
} else if (intensity < shadowThreshold) { // Shadow adjustments for cool/film-like tint
g += shadowGreenAdd; // Adds slight green to shadows
b += shadowBlueAdd; // Adds slight blue to shadows
}
// Clamp after selective H/S adjustments
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 5. Film Grain Simulation
if (grainAmount > 0) {
// Generate random noise value between -grainAmount and +grainAmount
const grain = (Math.random() - 0.5) * grainAmount * 2;
r += grain;
g += grain;
b += grain;
}
// Final clamp for all channels
data[i] = Math.max(0, Math.min(255, r));
data[i + 1] = Math.max(0, Math.min(255, g));
data[i + 2] = Math.max(0, Math.min(255, b));
// Alpha channel (data[i + 3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes