You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, roofColor = "#8b3a3a", doorColor = "#4a3018", skyColor = "#e0f7fa", yardColor = "#7cb342") {
const canvas = document.createElement('canvas');
const w = originalImg.width;
const h = originalImg.height;
// Scale parameters based on image size to maintain aesthetics over any resolution
const pad = Math.min(w, h) * 0.1;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Fill Sky Background
if (skyColor !== "transparent") {
ctx.fillStyle = skyColor;
ctx.fillRect(0, 0, w, h);
}
// Define structural coordinates
const peakY = pad * 1.5;
const roofY = h * 0.4;
const leftX = pad * 1.5;
const rightX = w - pad * 1.5;
const bottomY = h - pad;
const wallW = rightX - leftX;
const wallH = bottomY - roofY;
// Fill Yard Background
if (yardColor !== "transparent") {
const horizonY = bottomY - wallH * 0.2;
ctx.fillStyle = yardColor;
ctx.fillRect(0, horizonY, w, h - horizonY);
}
// Stroke Configuration
const lineW = Math.max(2, Math.min(w, h) * 0.005);
ctx.lineWidth = lineW;
ctx.strokeStyle = "#222";
ctx.lineJoin = 'round';
// 1. Draw Chimney (before roof so roof overlaps base)
const chimW = wallW * 0.15;
const chimX = rightX - chimW - wallW * 0.1;
const chimY = peakY - pad * 0.2;
const chimH = roofY - chimY;
ctx.fillStyle = doorColor; // Reuse door texture for chimney
ctx.fillRect(chimX, chimY, chimW, chimH);
ctx.strokeRect(chimX, chimY, chimW, chimH);
// 2. Map Original Image into House Wall Shape
ctx.save();
const houseWallPath = new Path2D();
houseWallPath.moveTo(leftX, bottomY);
houseWallPath.lineTo(leftX, roofY);
houseWallPath.lineTo(w / 2, peakY);
houseWallPath.lineTo(rightX, roofY);
houseWallPath.lineTo(rightX, bottomY);
houseWallPath.closePath();
// Clip onto wall and draw original image as the house's texture
ctx.clip(houseWallPath);
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.restore();
// Outline the wall
ctx.stroke(houseWallPath);
// 3. Draw Roof Overlay
const overhangX = pad * 0.8;
const overhangY = pad * 0.5;
const roofPath = new Path2D();
roofPath.moveTo(leftX, roofY);
roofPath.lineTo(w / 2, peakY);
roofPath.lineTo(rightX, roofY);
roofPath.lineTo(rightX + overhangX, roofY + overhangY);
roofPath.lineTo(w / 2, peakY - overhangY * 1.5); // Extended outer roof peak
roofPath.lineTo(leftX - overhangX, roofY + overhangY);
roofPath.closePath();
ctx.fillStyle = roofColor;
ctx.fill(roofPath);
ctx.stroke(roofPath);
// Draw Roof Tiles/Lines
ctx.save();
ctx.clip(roofPath);
ctx.strokeStyle = "rgba(0, 0, 0, 0.3)";
ctx.lineWidth = Math.max(1, w * 0.002);
for (let i = 0; i < roofY + overhangY; i += pad * 0.15) {
ctx.beginPath();
ctx.moveTo(0, i);
ctx.lineTo(w, i);
ctx.stroke();
}
ctx.restore();
// Restroke main path cleanly
ctx.lineWidth = lineW;
ctx.strokeStyle = "#222";
ctx.stroke(roofPath);
// 4. Draw Base Foundation
const foundH = pad * 0.4;
ctx.fillStyle = "#888";
ctx.fillRect(leftX - pad * 0.2, bottomY, wallW + pad * 0.4, foundH);
ctx.strokeRect(leftX - pad * 0.2, bottomY, wallW + pad * 0.4, foundH);
// 5. Draw Door
const doorW = wallW * 0.22;
const doorH = wallH * 0.4;
const doorX = (w / 2) - (doorW / 2);
const doorY = bottomY - doorH;
ctx.fillStyle = doorColor;
ctx.fillRect(doorX, doorY, doorW, doorH);
ctx.strokeRect(doorX, doorY, doorW, doorH);
// Draw Entry Path
ctx.fillStyle = "#A9A9A9";
ctx.beginPath();
ctx.moveTo(doorX, bottomY + foundH);
ctx.lineTo(w / 2 - doorW, h);
ctx.lineTo(w / 2 + doorW, h);
ctx.lineTo(doorX + doorW, bottomY + foundH);
ctx.closePath();
ctx.fill();
ctx.stroke();
// Doorknob
const knobR = doorW * 0.08;
ctx.fillStyle = "#FFC107";
ctx.beginPath();
ctx.arc(doorX + doorW - doorW * 0.25, doorY + doorH * 0.55, knobR, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Door Engraved Panels
const panelW = doorW * 0.5;
const panelH = doorH * 0.25;
const panelX = doorX + (doorW - panelW) / 2;
ctx.strokeRect(panelX, doorY + doorH * 0.1, panelW, panelH);
ctx.strokeRect(panelX, doorY + doorH * 0.45, panelW, panelH);
// 6. Draw Windows
const winW = wallW * 0.22;
const winH = wallH * 0.3;
const winY = roofY + wallH * 0.15;
const windowColor = "rgba(173, 216, 230, 0.85)";
function drawWindow(x) {
ctx.fillStyle = windowColor;
ctx.fillRect(x, winY, winW, winH);
ctx.strokeRect(x, winY, winW, winH);
ctx.beginPath();
// Structural mullion & transom (window cross)
ctx.moveTo(x + winW / 2, winY);
ctx.lineTo(x + winW / 2, winY + winH);
ctx.moveTo(x, winY + winH / 2);
ctx.lineTo(x + winW, winY + winH / 2);
ctx.stroke();
// Window sills
ctx.fillStyle = doorColor;
const sillW = winW * 1.2;
const sillH = pad * 0.15;
const sillX = x - (sillW - winW) / 2;
const sillY = winY + winH;
ctx.fillRect(sillX, sillY, sillW, sillH);
ctx.strokeRect(sillX, sillY, sillW, sillH);
}
// Standard left & right wall windows
const leftWinX = leftX + wallW * 0.1;
const rightWinX = rightX - wallW * 0.1 - winW;
drawWindow(leftWinX);
drawWindow(rightWinX);
// Feature Attic Window
const atticR = Math.min(winW * 0.4, (roofY - peakY) * 0.3);
const atticY = peakY + (roofY - peakY) * 0.6;
ctx.fillStyle = windowColor;
ctx.beginPath();
ctx.arc(w / 2, atticY, atticR, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(w / 2 - atticR, atticY);
ctx.lineTo(w / 2 + atticR, atticY);
ctx.moveTo(w / 2, atticY - atticR);
ctx.lineTo(w / 2, atticY + atticR);
ctx.stroke();
// Extra styling frame
ctx.beginPath();
ctx.arc(w / 2, atticY, atticR, 0, Math.PI * 2);
ctx.lineWidth = lineW * 1.5;
ctx.stroke();
return canvas;
}
Apply Changes