You can edit the below JavaScript code to customize the image tool.
/**
* Creates a visual representation of an image's monophonic pitch map.
* The function scans the image column by column, calculates the average brightness for each,
* and maps this brightness to a quantized pitch level. The result is a line graph
* on a gradient background, where the x-axis represents the image's width (time) and
* the y-axis represents the calculated pitch.
*
* @param {Image} originalImg The original source Image object.
* @param {number} [pitchLevels=24] The number of discrete pitch levels to quantize the brightness into.
* @param {string} [lineColor='#FFFFFF'] The CSS color of the pitch line graph.
* @param {string} [gradientStartColor='#000033'] The CSS color for the bottom of the background gradient (low pitch).
* @param {string} [gradientEndColor='#66CCFF'] The CSS color for the top of the background gradient (high pitch).
* @returns {HTMLCanvasElement} A canvas element displaying the pitch map visualization.
*/
function processImage(originalImg, pitchLevels = 24, lineColor = '#FFFFFF', gradientStartColor = '#000033', gradientEndColor = '#66CCFF') {
// 1. Create a temporary canvas to draw the original image for pixel access.
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
tempCanvas.width = width;
tempCanvas.height = height;
tempCtx.drawImage(originalImg, 0, 0);
let imageData;
try {
imageData = tempCtx.getImageData(0, 0, width, height);
} catch (e) {
// Handle potential security errors if the image source is cross-origin.
console.error("Could not get image data. Ensure the image is not from a different origin or is CORS-enabled.", e);
const errorCanvas = document.createElement('canvas');
errorCanvas.width = width > 0 ? width : 400;
errorCanvas.height = 150;
const errorCtx = errorCanvas.getContext('2d');
errorCtx.fillStyle = '#FFF';
errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
errorCtx.fillStyle = 'red';
errorCtx.font = '16px sans-serif';
errorCtx.textAlign = 'center';
errorCtx.textBaseline = 'middle';
errorCtx.fillText('Error: Could not process cross-origin image.', errorCanvas.width / 2, errorCanvas.height / 2);
return errorCanvas;
}
const data = imageData.data;
// 2. Create the output canvas for the pitch map visualization.
const outputCanvas = document.createElement('canvas');
// Use { alpha: false } for better performance when transparency is not needed.
const outputCtx = outputCanvas.getContext('2d', { alpha: false });
outputCanvas.width = width;
// Use a fixed height for a consistent and clear visualization.
const outputHeight = 256;
outputCanvas.height = outputHeight;
// 3. Draw the background gradient representing the pitch range (low to high).
const gradient = outputCtx.createLinearGradient(0, 0, 0, outputHeight);
gradient.addColorStop(0, gradientEndColor); // Top, represents high pitch
gradient.addColorStop(1, gradientStartColor); // Bottom, represents low pitch
outputCtx.fillStyle = gradient;
outputCtx.fillRect(0, 0, width, outputHeight);
// 4. Draw horizontal grid lines to visualize the quantized pitch levels.
outputCtx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
outputCtx.lineWidth = 1;
for (let i = 0; i <= pitchLevels; i++) {
const y = Math.round(outputHeight * (1 - i / pitchLevels));
outputCtx.beginPath();
// Offset by 0.5 for sharper 1px lines.
outputCtx.moveTo(0, y + 0.5);
outputCtx.lineTo(width, y + 0.5);
outputCtx.stroke();
}
// 5. Process image columns to calculate and store pitch points.
const pitchPoints = [];
if (width > 0 && height > 0) {
for (let x = 0; x < width; x++) {
let columnTotalBrightness = 0;
for (let y = 0; y < height; y++) {
const i = (y * width + x) * 4;
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Use the standard luminosity formula for perceptually accurate brightness.
const brightness = 0.299 * r + 0.587 * g + 0.114 * b;
columnTotalBrightness += brightness;
}
const averageBrightness = columnTotalBrightness / height;
// Quantize the average brightness [0, 255] into discrete pitch levels.
let level = Math.floor((averageBrightness / 255) * pitchLevels);
// Clamp the value to ensure it's within the valid range [0, pitchLevels-1].
level = Math.max(0, Math.min(pitchLevels - 1, level));
// Map the pitch level to a y-coordinate, centered within its band.
const yPos = outputHeight * (1 - (level + 0.5) / pitchLevels);
pitchPoints.push({ x: x, y: yPos });
}
}
// 6. Draw the pitch line graph on the canvas.
if (pitchPoints.length > 1) {
outputCtx.beginPath();
outputCtx.moveTo(pitchPoints[0].x, pitchPoints[0].y);
for (let i = 1; i < pitchPoints.length; i++) {
outputCtx.lineTo(pitchPoints[i].x, pitchPoints[i].y);
}
outputCtx.strokeStyle = lineColor;
outputCtx.lineWidth = 2;
outputCtx.lineJoin = 'round';
outputCtx.lineCap = 'round';
outputCtx.stroke();
}
// 7. Return the final canvas element.
return outputCanvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Gradient Map Monophonic Pitch Tool visualizes an image’s brightness as a line graph of pitch levels. By scanning the image column by column, it calculates the average brightness and maps this to quantized pitch levels, resulting in a gradient background with a monochrome pitch line. This tool can be useful for artists, musicians, and educators examining the relationship between visual elements and musical concepts, such as analyzing visual artwork for sound design inspirations or understanding how imagery can represent auditory qualities.