You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, tvColor = "#b03a2e", panelColor = "#d0d3d4", addScanlines = 1) {
// Determine bounds to keep the final canvas size optimized
const maxDim = 800;
let scale = 1;
if (originalImg.width > maxDim || originalImg.height > maxDim) {
scale = Math.min(maxDim / originalImg.width, maxDim / originalImg.height);
}
const vW = originalImg.width * scale;
const vH = originalImg.height * scale;
// Scale unit based on the geometric mean of dimensions for perfectly sized UI elements
const scaleUnit = Math.sqrt(vW * vH);
// Calculate TV component dimensions relative to image scale
const padding = Math.max(20, scaleUnit * 0.08);
const panelWidth = Math.max(50, scaleUnit * 0.25);
const antennaH = Math.max(30, scaleUnit * 0.2);
const legH = Math.max(15, scaleUnit * 0.1);
const tvW = padding + vW + padding + panelWidth + padding;
const tvH = padding + vH + padding;
// Canvas setup with margins to ensure elements like antennas/legs don't clip
const canvas = document.createElement("canvas");
const marginX = padding;
canvas.width = tvW + marginX * 2;
canvas.height = antennaH + tvH + legH;
const ctx = canvas.getContext("2d");
// Helper function for drawing rounded rectangles uniformly
function drawRoundRect(x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
}
// --- 1. Draw Antennas (in background) ---
const baseX = marginX + tvW / 2;
const baseY = antennaH;
const tx1 = baseX - scaleUnit * 0.3;
const ty1 = antennaH * 0.2;
const tx2 = baseX + scaleUnit * 0.3;
const ty2 = antennaH * 0.2;
ctx.strokeStyle = "#7f8c8d";
ctx.lineWidth = Math.max(3, scaleUnit * 0.015);
ctx.lineCap = "round";
ctx.beginPath(); // Antenna 1
ctx.moveTo(baseX, baseY);
ctx.lineTo(tx1, ty1);
ctx.stroke();
ctx.beginPath(); // Antenna 2
ctx.moveTo(baseX, baseY);
ctx.lineTo(tx2, ty2);
ctx.stroke();
// Antenna Balls
ctx.fillStyle = "#95a5a6";
ctx.beginPath();
ctx.arc(tx1, ty1, Math.max(4, scaleUnit * 0.025), 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(tx2, ty2, Math.max(4, scaleUnit * 0.025), 0, Math.PI * 2);
ctx.fill();
// --- 2. Draw Legs (in background) ---
const legBaseY = antennaH + tvH - scaleUnit * 0.05; // Placed slightly up so they sit "behind" the body
const lLegX1 = marginX + tvW * 0.2;
const lLegX2 = lLegX1 - scaleUnit * 0.15;
const rLegX1 = marginX + tvW * 0.8;
const rLegX2 = rLegX1 + scaleUnit * 0.15;
const legTipY = antennaH + tvH + legH;
ctx.strokeStyle = tvColor;
ctx.lineWidth = Math.max(6, scaleUnit * 0.04);
ctx.lineCap = "round";
ctx.beginPath(); // Left Leg
ctx.moveTo(lLegX1, legBaseY);
ctx.lineTo(lLegX2, legTipY);
ctx.stroke();
ctx.beginPath(); // Right Leg
ctx.moveTo(rLegX1, legBaseY);
ctx.lineTo(rLegX2, legTipY);
ctx.stroke();
// --- 3. Draw Main TV Body ---
const tvRadius = Math.max(10, scaleUnit * 0.06);
drawRoundRect(marginX, antennaH, tvW, tvH, tvRadius);
ctx.fillStyle = tvColor;
ctx.fill();
// Outer bold border for icon look
ctx.strokeStyle = "#2c3e50";
ctx.lineWidth = Math.max(2, scaleUnit * 0.012);
ctx.stroke();
// Inner outline to give depth
drawRoundRect(marginX + 2, antennaH + 2, tvW - 4, tvH - 4, tvRadius);
ctx.strokeStyle = "rgba(0, 0, 0, 0.15)";
ctx.stroke();
// --- 4. Draw Right Control Panel ---
const panelX = marginX + padding + vW + padding;
const panelY = antennaH + padding;
const panelW = panelWidth;
const panelH = vH;
drawRoundRect(panelX, panelY, panelW, panelH, tvRadius * 0.5);
ctx.fillStyle = panelColor;
ctx.fill();
// --- 5. Draw Knobs ---
const knobRadius = panelW * 0.3;
const knobX = panelX + panelW / 2;
const knob1Y = panelY + panelH * 0.2;
const knob2Y = panelY + panelH * 0.5;
// Knob 1 (Top)
ctx.fillStyle = "#34495e";
ctx.beginPath();
ctx.arc(knobX, knob1Y, knobRadius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "#ecf0f1";
ctx.beginPath();
ctx.arc(knobX, knob1Y, knobRadius * 0.7, 0, Math.PI * 2);
ctx.fill();
// Indicator
ctx.strokeStyle = "#c0392b";
ctx.lineWidth = Math.max(2, scaleUnit * 0.015);
ctx.beginPath();
ctx.moveTo(knobX, knob1Y);
ctx.lineTo(knobX + Math.cos(Math.PI/4) * knobRadius * 0.7, knob1Y - Math.sin(Math.PI/4) * knobRadius * 0.7);
ctx.stroke();
// Knob 2 (Middle)
ctx.fillStyle = "#34495e";
ctx.beginPath();
ctx.arc(knobX, knob2Y, knobRadius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "#ecf0f1";
ctx.beginPath();
ctx.arc(knobX, knob2Y, knobRadius * 0.7, 0, Math.PI * 2);
ctx.fill();
// Indicator
ctx.strokeStyle = "#c0392b";
ctx.beginPath();
ctx.moveTo(knobX, knob2Y);
ctx.lineTo(knobX - Math.cos(Math.PI/6) * knobRadius * 0.7, knob2Y - Math.sin(Math.PI/6) * knobRadius * 0.7);
ctx.stroke();
// --- 6. Draw Speaker Grill ---
const grillStartY = panelY + panelH * 0.75;
const grillStep = Math.max(5, scaleUnit * 0.03);
const grillLines = Math.floor((panelH * 0.2) / grillStep);
ctx.strokeStyle = "#2c3e50";
ctx.lineWidth = Math.max(2, scaleUnit * 0.015);
ctx.lineCap = "round";
for (let i = 0; i < grillLines; i++) {
const y = grillStartY + i * grillStep;
ctx.beginPath();
ctx.moveTo(panelX + panelW * 0.2, y);
ctx.lineTo(panelX + panelW * 0.8, y);
ctx.stroke();
}
// --- 7. Draw Screen Bezel ---
const bezelPadding = Math.max(5, scaleUnit * 0.02);
drawRoundRect(
marginX + padding - bezelPadding,
antennaH + padding - bezelPadding,
vW + bezelPadding * 2,
vH + bezelPadding * 2,
tvRadius * 0.5
);
ctx.fillStyle = "#111111"; // Thick retro border
ctx.fill();
// --- 8. Render Screen Picture (the original image) ---
ctx.save();
drawRoundRect(marginX + padding, antennaH + padding, vW, vH, tvRadius * 0.3);
// Dark background behind image, mainly for transparent pictures
ctx.fillStyle = "#000000";
ctx.fill();
// Clip inner content mathematically to the rounded screen borders
ctx.clip();
ctx.drawImage(originalImg, marginX + padding, antennaH + padding, vW, vH);
// --- 9. Apply TV Static / Screen Glass Effects ---
if (Number(addScanlines) === 1) {
// Pixel scanlines
ctx.fillStyle = "rgba(0, 0, 0, 0.18)";
const scanlineThickness = Math.max(1, Math.floor(scaleUnit * 0.005));
const scanlineGap = scanlineThickness * 2;
for (let y = 0; y < vH; y += scanlineGap + scanlineThickness) {
ctx.fillRect(marginX + padding, antennaH + padding + y, vW, scanlineThickness);
}
// Curved screen glare reflex
ctx.fillStyle = "rgba(255, 255, 255, 0.1)";
ctx.beginPath();
ctx.moveTo(marginX + padding, antennaH + padding);
ctx.lineTo(marginX + padding + vW * 0.9, antennaH + padding);
ctx.lineTo(marginX + padding + vW * 0.3, antennaH + padding + vH);
ctx.lineTo(marginX + padding, antennaH + padding + vH);
ctx.fill();
}
ctx.restore();
return canvas;
}
Apply Changes