Body photos are personal. If you are only trying a design on your forearm or wrist, there is no good reason that picture should leave the device just to draw one image on top of another.
A tattoo try-on preview is mostly 2D compositing: a body photo as the base layer, a design (ideally with a transparent background) as the overlay, then pan, scale, rotate, and opacity. The Canvas 2D API can do all of that locally.
The two-layer model
Keep two images in memory: the body photo and the tattoo artwork. Draw the photo first, then draw the design with a transform:
function drawPreview(ctx, body, tattoo, { x, y, scale, angle, opacity }) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(body, 0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.scale(scale, scale);
ctx.globalAlpha = opacity;
ctx.drawImage(tattoo, -tattoo.width / 2, -tattoo.height / 2);
ctx.restore();
}
Pointer events update x / y. A slider updates scale, angle, or opacity. Redraw on every change. That is enough for “move it, make it bigger, turn it a few degrees, and see how heavy the ink looks.”
globalAlpha is a preview control, not a prediction of healed ink. It helps someone compare a softer overlay with a stronger one; it does not tell you how dense a real tattoo will be.
Transparency and white backgrounds
Design files work best as PNG/WebP with a real alpha channel. If someone uploads a design on a white background, a simple chroma-style pass (treat near-white pixels as transparent) is often enough to keep a paper-scan from covering the skin. It will not beat a proper transparent export, but it stops the preview from looking like a white sticker.
What this model cannot do
A flat overlay cannot wrap around an ankle, follow the curve of a shoulder, or show how a design will look after healing. Perspective is whatever the photograph already has. If the photo is cropped too tightly, it also becomes hard to judge proportion against nearby landmarks (wrist, elbow, knuckles).
Those limits are worth saying out loud. A browser preview is a conversation starter for you and a tattoo artist, not a stencil and not a promise.
Privacy by keeping pixels local
Because the composition never needs a server, the photo and the design can stay in File / ImageBitmap objects and, if you want a short-lived draft, in the browser’s local storage. No account is required for the preview itself. That matters more for a body photo than for a typical screenshot app.
If you want to try this kind of preview without writing the canvas code, I built TatTryOn: a free Classic simulator that runs in the browser (no sign-up; photos stay on your device), plus an optional AI tattoo generator when you want a new design to place. The preview is a flat overlay with blending, and it says so.
Happy to talk through canvas gotchas — hit-testing a rotated overlay, exporting the before/after, or keeping large photos from choking mobile browsers — in the comments.
Top comments (0)