You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, message = "Hidden Message Inside!") {
// Helper function to draw error messages on a canvas
function _drawErrorOnCanvas(canvasToDrawOn, width, height, errorMessage) {
// Ensure canvas has valid dimensions
if (!canvasToDrawOn) {
canvasToDrawOn = document.createElement('canvas');
}
// Use provided width/height, or defaults if they are invalid
canvasToDrawOn.width = width > 0 ? width : 200;
canvasToDrawOn.height = height > 0 ? height : 100;
const ctx = canvasToDrawOn.getContext('2d');
ctx.clearRect(0, 0, canvasToDrawOn.width, canvasToDrawOn.height); // Clear previous content
ctx.fillStyle = 'lightgray';
ctx.fillRect(0, 0, canvasToDrawOn.width, canvasToDrawOn.height);
ctx.fillStyle = 'red';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const words = errorMessage.split(' ');
let line = '';
const lines = [];
const maxWidth = canvasToDrawOn.width * 0.9; // Max width of text line
for (let n = 0; n < words.length; n++) {
const testLine = line + words[n] + ' ';
const metrics = ctx.measureText(testLine); // Measure text width
if (metrics.width > maxWidth && n > 0) {
lines.push(line.trim());
line = words[n] + ' ';
} else {
line = testLine;
}
}
lines.push(line.trim()); // Add the last line
const lineHeight = 20; // Approximate line height
const totalTextHeight = lines.length * lineHeight;
// Calculate starting Y position to center the block of text vertically.
// startY will be the y-coordinate for the middle of the first line.
let startY = (canvasToDrawOn.height - totalTextHeight) / 2 + lineHeight / 2;
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], canvasToDrawOn.width / 2, startY + (i * lineHeight));
}
return canvasToDrawOn;
}
// Validate the input image object
if (!(originalImg instanceof HTMLImageElement) || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
// Create a new canvas for the error message if originalImg is problematic
let errorCanvas = document.createElement('canvas');
return _drawErrorOnCanvas(errorCanvas, 200, 100, 'Error: Invalid or unloaded image provided. Ensure the image is fully loaded.');
}
const canvas = document.createElement('canvas');
canvas.width = originalImg.naturalWidth; // Use actual image dimensions
canvas.height = originalImg.naturalHeight;
// The willReadFrequently hint can optimize getImageData calls.
const ctx = canvas.getContext('2d', { willReadFrequently: true });
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
console.error("Error getting ImageData:", e);
let errorMsg = 'Error: Could not process image pixels.';
if (e.name === 'SecurityError') {
errorMsg = 'Error: Cross-origin image security policy prevents pixel access. The image might be from another website or a local file accessed without proper permissions.';
}
// Cannot draw on the original canvas if it's tainted by cross-origin data.
// Create a new, clean canvas for the error message.
let errorCanvas = document.createElement('canvas');
return _drawErrorOnCanvas(errorCanvas, canvas.width, canvas.height, errorMsg);
}
const data = imageData.data; // Pixel data: R,G,B,A, R,G,B,A, ...
const encoder = new TextEncoder(); // Standard API to convert string to UTF-8 bytes
const messageBytes = encoder.encode(message);
const BITS_FOR_LENGTH_HEADER = 32; // Using 32 bits (4 bytes) to store the length of the message.
// This length is the number of *bytes* in the UTF-8 encoded message.
// Calculate total available bits using LSB of R,G,B channels
const totalAvailableDataBits = canvas.width * canvas.height * 3;
if (totalAvailableDataBits < BITS_FOR_LENGTH_HEADER) {
console.error("Image is too small to store even the message length header.");
// Draw error on the main canvas (it's usable since getImageData succeeded)
return _drawErrorOnCanvas(canvas, canvas.width, canvas.height, "Image is too small to hide message (cannot fit length header).");
}
// Maximum number of message bytes that can be stored after the length header
const maxMessagePayloadBytes = Math.floor((totalAvailableDataBits - BITS_FOR_LENGTH_HEADER) / 8);
let actualMessageBytesToEncode;
if (maxMessagePayloadBytes <= 0) {
actualMessageBytesToEncode = new Uint8Array(0); // No space for payload, encode empty message.
if (messageBytes.length > 0) {
console.warn(`Image has no space for message payload (only ${maxMessagePayloadBytes} bytes available after header). Encoding an empty message.`);
}
} else if (messageBytes.length > maxMessagePayloadBytes) {
console.warn(`Message is too long. Original length: ${messageBytes.length} bytes. Maximum allowed: ${maxMessagePayloadBytes} bytes. Message will be truncated.`);
actualMessageBytesToEncode = messageBytes.slice(0, maxMessagePayloadBytes);
} else {
actualMessageBytesToEncode = messageBytes;
}
const actualMessageLengthInBytes = actualMessageBytesToEncode.length;
let dataArrIndex = 0; // Current index in the imageData.data array, points to an R, G, or B component.
// Helper to modify the Least Significant Bit (LSB) of a color component
const setLSB = (value, bit) => (bit === 1 ? (value | 1) : (value & 0xFE));
// 1. Encode message length (number of BYTES in the message) into LSBs
for (let i = 0; i < BITS_FOR_LENGTH_HEADER; i++) {
if (dataArrIndex >= data.length) {
console.error("Critical Error: Ran out of image data while writing length header. This should have been caught by capacity checks.");
return _drawErrorOnCanvas(canvas, canvas.width, canvas.height, "Internal error: Ran out of space for message header. Image might be too small.");
}
// Get the i-th bit of the length (MSB first)
const bit = (actualMessageLengthInBytes >> (BITS_FOR_LENGTH_HEADER - 1 - i)) & 1;
data[dataArrIndex] = setLSB(data[dataArrIndex], bit);
dataArrIndex++; // Move to next color component (e.g., R to G)
// imageData.data is [R,G,B,A, R,G,B,A, ...]. Alpha channels (at indices 3, 7, 11, ...) are skipped.
// If dataArrIndex was pointing to B (e.g. index 2), after increment it points to A (index 3).
// (dataArrIndex + 1) % 4 === 0 means dataArrIndex is currently an Alpha channel index. This is slightly off.
// Correct logic: if current dataArrIndex is an R or G, next is G or B. If current is B, next is A. After modifying B, dataArrIndex points to A. Skip A.
if ((dataArrIndex + 1) % 4 === 0) { // If dataArrIndex is now an Alpha channel's predecessor (B)
// after this write dataArrIndex points to current Alpha
dataArrIndex++; // Skip Alpha channel by incrementing index again.
}
}
// 2. Encode message content (bytes) into LSBs
for (let byteIdx = 0; byteIdx < actualMessageBytesToEncode.length; byteIdx++) {
const currentByte = actualMessageBytesToEncode[byteIdx];
for (let bitIdx = 0; bitIdx < 8; bitIdx++) { // Iterate through 8 bits of the current byte
if (dataArrIndex >= data.length) {
console.error("Critical Error: Ran out of image data while writing message body. Message partially encoded.");
ctx.putImageData(imageData, 0, 0); // Save whatever was encoded
return _drawErrorOnCanvas(canvas, canvas.width, canvas.height, "Internal error: Ran out of space for full message body. Message partially hidden.");
}
const bit = (currentByte >> (7 - bitIdx)) & 1; // Get bit from byte (MSB first)
data[dataArrIndex] = setLSB(data[dataArrIndex], bit);
dataArrIndex++;
if ((dataArrIndex + 1) % 4 === 0) {
dataArrIndex++;
}
}
}
// Write the modified pixel data back to the canvas
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Hidden Message Filter Effect Tool allows users to embed hidden messages within images by manipulating the pixel data of the original image. This tool can be utilized for various purposes, such as creating unique digital art, embedding messages in photos for personal use, or for educational purposes to teach about steganography. Users can input a message, and the tool will modify the least significant bits of the image’s pixel data to conceal the message, thereby creating a new image that appears visually unchanged. This feature can be particularly useful for sending secure communications or for creating puzzles and games.