You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, presetAlarmTime = "07:00", clockFormat = "digital") {
// Create the main container
const container = document.createElement('div');
container.style.position = 'relative';
container.style.display = 'inline-block';
container.style.fontFamily = '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif';
container.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)';
container.style.borderRadius = '10px';
container.style.overflow = 'hidden';
// Create the canvas for drawing the image and clock
const canvas = document.createElement('canvas');
const canvasWidth = originalImg.width;
const canvasHeight = originalImg.height;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
canvas.style.display = 'block';
canvas.style.maxWidth = '100%';
canvas.style.height = 'auto';
container.appendChild(canvas);
const ctx = canvas.getContext('2d');
// Create the UI overlay
const uiContainer = document.createElement('div');
uiContainer.style.position = 'absolute';
uiContainer.style.bottom = '5%';
uiContainer.style.left = '50%';
uiContainer.style.transform = 'translateX(-50%)';
uiContainer.style.background = 'rgba(0, 0, 0, 0.75)';
uiContainer.style.padding = '12px 20px';
uiContainer.style.borderRadius = '12px';
uiContainer.style.display = 'flex';
uiContainer.style.gap = '12px';
uiContainer.style.alignItems = 'center';
uiContainer.style.flexWrap = 'wrap';
uiContainer.style.justifyContent = 'center';
uiContainer.style.backdropFilter = 'blur(4px)';
const timeInput = document.createElement('input');
timeInput.type = 'time';
timeInput.value = presetAlarmTime;
timeInput.style.border = 'none';
timeInput.style.borderRadius = '6px';
timeInput.style.padding = '8px';
timeInput.style.fontSize = '16px';
timeInput.style.outline = 'none';
timeInput.style.cursor = 'text';
const toggleBtn = document.createElement('button');
toggleBtn.textContent = 'Set Alarm';
toggleBtn.style.padding = '8px 16px';
toggleBtn.style.fontSize = '16px';
toggleBtn.style.backgroundColor = '#4caf50';
toggleBtn.style.color = '#ffffff';
toggleBtn.style.border = 'none';
toggleBtn.style.borderRadius = '6px';
toggleBtn.style.cursor = 'pointer';
toggleBtn.style.fontWeight = 'bold';
toggleBtn.style.transition = 'background-color 0.2s';
uiContainer.appendChild(timeInput);
uiContainer.appendChild(toggleBtn);
container.appendChild(uiContainer);
// Alarm state variables
let alarmSet = false;
let alarmRinging = false;
let audioCtx = null;
let beepInterval = null;
function playAlarm() {
if (!audioCtx && typeof window.AudioContext !== 'undefined') {
const AudioContext = window.AudioContext || window.webkitAudioContext;
audioCtx = new AudioContext();
}
if (audioCtx && audioCtx.state === 'suspended') {
audioCtx.resume();
}
function beep() {
if (!alarmRinging || !audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'square';
osc.frequency.setValueAtTime(880, audioCtx.currentTime); // 880Hz beep
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(audioCtx.currentTime);
osc.stop(audioCtx.currentTime + 0.15); // Beep for 150ms
setTimeout(() => {
if (!alarmRinging) return;
const osc2 = audioCtx.createOscillator();
const gain2 = audioCtx.createGain();
osc2.type = 'square';
osc2.frequency.setValueAtTime(880, audioCtx.currentTime);
osc2.connect(gain2);
gain2.connect(audioCtx.destination);
osc2.start(audioCtx.currentTime);
osc2.stop(audioCtx.currentTime + 0.15);
}, 250);
}
beep();
beepInterval = setInterval(beep, 1000);
}
function stopAlarm() {
alarmRinging = false;
if (beepInterval) {
clearInterval(beepInterval);
beepInterval = null;
}
}
toggleBtn.addEventListener('click', () => {
if (alarmRinging) {
stopAlarm();
toggleBtn.textContent = 'Set Alarm';
toggleBtn.style.backgroundColor = '#4caf50';
alarmSet = false;
} else if (alarmSet) {
alarmSet = false;
toggleBtn.textContent = 'Set Alarm';
toggleBtn.style.backgroundColor = '#4caf50';
} else {
if (!timeInput.value) return;
alarmSet = true;
toggleBtn.textContent = 'Cancel Alarm';
toggleBtn.style.backgroundColor = '#ff9800';
// Initialize/Resume AudioContext on user interaction
if (!audioCtx && typeof window.AudioContext !== 'undefined') {
const AudioContext = window.AudioContext || window.webkitAudioContext;
audioCtx = new AudioContext();
}
if (audioCtx && audioCtx.state === 'suspended') {
audioCtx.resume();
}
}
});
let frameId;
let flash = false;
let lastFlashTime = 0;
function render() {
// Draw image background
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(originalImg, 0, 0);
// Darken overlay for better clock visibility
ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
// Check alarm match
if (alarmSet && !alarmRinging) {
const [alarmH, alarmM] = timeInput.value.split(':').map(Number);
if (hours === alarmH && minutes === alarmM) {
alarmRinging = true;
alarmSet = false;
toggleBtn.textContent = 'Stop Alarm!';
toggleBtn.style.backgroundColor = '#f44336';
playAlarm();
}
}
// Flashing screen effect when ringing
if (alarmRinging) {
const timeSinceFlash = Date.now() - lastFlashTime;
if (timeSinceFlash > 400) {
flash = !flash;
lastFlashTime = Date.now();
}
if (flash) {
ctx.fillStyle = 'rgba(255, 0, 0, 0.25)';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
}
}
const minDim = Math.min(canvasWidth, canvasHeight);
const centerX = canvasWidth / 2;
const centerY = canvasHeight / 2 - (minDim * 0.05);
// Draw Clock
if (clockFormat.toLowerCase() === 'analog') {
const radius = Math.max(minDim * 0.35, 60);
// Shadow for contrast
ctx.shadowColor = "rgba(0, 0, 0, 0.8)";
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
// Draw Face
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
ctx.fill();
ctx.lineWidth = radius * 0.05;
ctx.strokeStyle = '#222';
ctx.stroke();
ctx.shadowColor = "transparent";
// Draw Numbers
ctx.font = `bold ${radius * 0.25}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#222';
for (let i = 1; i <= 12; i++) {
const angle = (i * Math.PI / 6) - (Math.PI / 2);
const x = centerX + Math.cos(angle) * radius * 0.78;
const y = centerY + Math.sin(angle) * radius * 0.78;
ctx.fillText(i.toString(), x, y);
}
// Draw Hands Helper
function drawHand(value, max, length, width, color) {
const angle = (value * 2 * Math.PI / max) - (Math.PI / 2);
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(centerX + Math.cos(angle) * length, centerY + Math.sin(angle) * length);
ctx.lineWidth = width;
ctx.strokeStyle = color;
ctx.lineCap = 'round';
ctx.stroke();
}
// Draw Time
drawHand((hours % 12) + minutes / 60, 12, radius * 0.5, radius * 0.08, '#222'); // Hour
drawHand(minutes + seconds / 60, 60, radius * 0.7, radius * 0.05, '#555'); // Minute
drawHand(seconds, 60, radius * 0.85, radius * 0.02, '#e74c3c'); // Second
// Center Pin
ctx.beginPath();
ctx.arc(centerX, centerY, radius * 0.05, 0, 2 * Math.PI);
ctx.fillStyle = '#222';
ctx.fill();
} else {
// Digital Clock
const timeStr = [
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
seconds.toString().padStart(2, '0')
].join(':');
const timeFontSize = Math.max(minDim * 0.18, 40);
ctx.font = `bold ${timeFontSize}px "Courier New", Courier, monospace`;
// Text shadow for high contrast over any image
ctx.shadowColor = "rgba(0, 0, 0, 0.9)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = (alarmRinging && flash) ? '#ff5252' : '#ffffff';
ctx.fillText(timeStr, centerX, centerY);
// Draw Date
const dateStr = now.toLocaleDateString();
ctx.font = `bold ${timeFontSize * 0.3}px sans-serif`;
ctx.fillStyle = '#ddd';
ctx.fillText(dateStr, centerX, centerY + timeFontSize * 0.8);
ctx.shadowColor = "transparent";
}
// Status Text (Top area)
ctx.font = `bold ${Math.max(minDim * 0.06, 16)}px sans-serif`;
ctx.textAlign = 'center';
ctx.shadowColor = "rgba(0, 0, 0, 0.8)";
ctx.shadowBlur = 5;
let statusText = '';
if (alarmRinging) {
ctx.fillStyle = '#ff5252';
statusText = 'WAKE UP! ALARM IS RINGING!';
} else if (alarmSet) {
ctx.fillStyle = '#69f0ae';
statusText = `Alarm Scheduled for ${timeInput.value}`;
}
if (statusText) {
const offsetMultiplier = clockFormat.toLowerCase() === 'analog' ? 0.38 : 0.25;
ctx.fillText(statusText, centerX, (canvasHeight / 2) - (minDim * offsetMultiplier));
}
ctx.shadowColor = "transparent";
frameId = requestAnimationFrame(render);
}
render();
// Ensure the intervals and animations are stopped if the element loses track, though in HTML usage returning the UI handles its lifecycle
return container;
}
Apply Changes