CMYK
I am gonna keep thinking of little projects for AI to try out. This was a fun one. There might even be a use for this... as soon as someone can figure one out.
cmyk.js
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.getElementById('imageCanvas');
const ctx = canvas.getContext('2d');
const container = document.getElementById('canvasContainer');
const downloadBtn = document.getElementById('downloadBtn');
// Controls
const dotSizeControl = document.getElementById('dotSize');
const dotSpacingControl = document.getElementById('dotSpacing');
const cyanAngleControl = document.getElementById('cyanAngle');
const magentaAngleControl = document.getElementById('magentaAngle');
const yellowAngleControl = document.getElementById('yellowAngle');
const blackAngleControl = document.getElementById('blackAngle');
// Settings
let settings = {
dotSize: parseFloat(dotSizeControl.value),
dotSpacing: parseInt(dotSpacingControl.value),
cyanAngle: parseInt(cyanAngleControl.value),
magentaAngle: parseInt(magentaAngleControl.value),
yellowAngle: parseInt(yellowAngleControl.value),
blackAngle: parseInt(blackAngleControl.value)
};
// Default image - updated to use local path
const defaultImage = new Image();
defaultImage.crossOrigin = "Anonymous";
defaultImage.src = "./www/p/cmyk/test.jpg"; // Local image path
let currentImage = defaultImage;
// Error handler for image loading
defaultImage.onerror = function() {
console.error("Error loading the default image. Check if the path is correct.");
// Fallback to a basic colored rectangle if image fails to load
canvas.width = 800;
canvas.height = 500;
ctx.fillStyle = 'lightblue';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Add text to inform user
ctx.fillStyle = 'black';
ctx.font = '20px Arial';
ctx.textAlign = 'center';
ctx.fillText('Default image failed to load.', canvas.width/2, canvas.height/2);
ctx.fillText('Please drag and drop an image.', canvas.width/2, canvas.height/2 + 30);
};
// Load default image
defaultImage.onload = function() {
processImage(defaultImage);
};
// Event listeners for drag and drop
container.addEventListener('dragover', function(e) {
e.preventDefault();
container.classList.add('active');
});
container.addEventListener('dragleave', function() {
container.classList.remove('active');
});
container.addEventListener('drop', function(e) {
e.preventDefault();
container.classList.remove('active');
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
const file = e.dataTransfer.files[0];
if (file.type.match('image.*')) {
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
currentImage = img;
processImage(img);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
}
});
// Process and convert image to CMYK dots
function processImage(img) {
// Set canvas dimensions to match image
canvas.width = img.width;
canvas.height = img.height;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw original image to get pixel data
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Clear canvas again for dot drawing
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw CMYK dots
drawCMYKDots(imageData);
}
// RGB to CMYK conversion
function rgbToCmyk(r, g, b) {
// Convert RGB to 0-1 range
r = r / 255;
g = g / 255;
b = b / 255;
// Find K (black)
let k = 1 - Math.max(r, g, b);
// Calculate CMY values
let c = k === 1 ? 0 : (1 - r - k) / (1 - k);
let m = k === 1 ? 0 : (1 - g - k) / (1 - k);
let y = k === 1 ? 0 : (1 - b - k) / (1 - k);
// Return CMYK values (0-1 range)
return {
c: c,
m: m,
y: y,
k: k
};
}
// Draw CMYK dots based on image data
function drawCMYKDots(imageData) {
const { dotSize, dotSpacing, cyanAngle, magentaAngle, yellowAngle, blackAngle } = settings;
// Adjust angles based on settings (convert to radians)
const angles = {
c: (cyanAngle * Math.PI) / 180,
m: (magentaAngle * Math.PI) / 180,
y: (yellowAngle * Math.PI) / 180,
k: (blackAngle * Math.PI) / 180
};
// Calculate dot grid offsets for each color
const offsets = {};
for (const color in angles) {
offsets[color] = {
x: Math.cos(angles[color]) * dotSpacing,
y: Math.sin(angles[color]) * dotSpacing
};
}
// Create separate pixel arrays for each CMYK channel
const width = imageData.width;
const height = imageData.height;
const pixels = imageData.data;
// Draw dots for each color channel
const colors = [
{ channel: 'c', color: 'rgba(0, 255, 255, 0.9)' },
{ channel: 'm', color: 'rgba(255, 0, 255, 0.9)' },
{ channel: 'y', color: 'rgba(255, 255, 0, 0.9)' },
{ channel: 'k', color: 'rgba(0, 0, 0, 0.9)' }
];
// Process each color channel
colors.forEach(({ channel, color }) => {
const offset = offsets[channel];
// Create dot grid pattern
for (let gridY = 0; gridY < height; gridY += dotSpacing) {
for (let gridX = 0; gridX < width; gridX += dotSpacing) {
// Calculate rotated positions
const x = Math.round(gridX + offset.x * (gridY / dotSpacing) % dotSpacing);
const y = Math.round(gridY + offset.y * (gridX / dotSpacing) % dotSpacing);
if (x >= 0 && x < width && y >= 0 && y < height) {
// Get pixel data
const i = (y * width + x) * 4;
const r = pixels[i];
const g = pixels[i + 1];
const b = pixels[i + 2];
// Convert to CMYK
const cmyk = rgbToCmyk(r, g, b);
// Draw dot based on CMYK value
const cmykValue = cmyk[channel];
// Calculate dot radius (directly proportional to CMYK value)
const radius = dotSize * cmykValue;
if (radius > 0.2) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.globalCompositeOperation = 'multiply';
ctx.fill();
}
}
}
}
});
// Reset composite operation
ctx.globalCompositeOperation = 'source-over';
}
// Control event listeners
const controls = [dotSizeControl, dotSpacingControl, cyanAngleControl,
magentaAngleControl, yellowAngleControl, blackAngleControl];
controls.forEach(control => {
control.addEventListener('input', function() {
settings[this.id] = parseFloat(this.value);
if (currentImage) {
processImage(currentImage);
}
});
});
// Download button
downloadBtn.addEventListener('click', function() {
const link = document.createElement('a');
link.download = 'cmyk-dots-image.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
});