Details & Tips
Rendering steps, in order: the canvas is set to your chosen width and height (each capped at 4000px — larger requests show an inline validation message rather than being attempted, keeping canvas allocation fast and safe), then filled edge-to-edge with the chosen background color using a single `fillRect(0, 0, width, height)` call. The label text is determined next: if the custom text field is empty (after trimming leading/trailing spaces), the tool falls back to `${width}×${height}` — for example "800×600" — using the actual multiplication sign character rather than a plain letter x, matching the conventional way image dimensions are written.
Font auto-sizing works as a shrink-to-fit loop. The starting font size is set proportionally to the canvas height — specifically `Math.floor(height / 6)` — which scales sensibly across very small and very large canvases alike (a 300px-tall canvas starts at 50px text, an 1200px-tall canvas starts at 200px text). With that starting size applied as a bold sans-serif font, the canvas's own `ctx.measureText(text).width` reports exactly how wide the text would render at that size; if that measured width exceeds 90% of the canvas width, the font size is reduced by 1px and re-measured, repeating until the text fits within that 90% budget or the font size hits a floor of 10px, whichever comes first — the 10% margin on either side keeps the text from touching the canvas edges even at the widest acceptable size, and the 10px floor guarantees the loop always terminates rather than shrinking indefinitely for an extremely long custom label on a small canvas (in that edge case, the text may still slightly overflow at the 10px floor, which is an accepted trade-off for guaranteeing the tool never hangs or produces invisible 0px text).
Worked example: on an 800×600 canvas showing the default text "800×600" (7 characters), the starting font size of `600 / 6 = 100px` typically measures comfortably under the 720px budget (90% of 800px) for a short numeric string like this, so no shrinking is needed and the text renders at the full 100px starting size. A longer custom label like "Product photography placeholder" (32 characters) on that same 800×600 canvas would measure far wider than 720px at 100px font size, so the loop steps the size down — 99px, 98px, and so on — until the measured width finally drops under 720px, landing at whatever smaller size actually fits that specific string's rendered width in a bold sans-serif font.
Once sized, the text is drawn with `ctx.textAlign = 'center'` and `ctx.textBaseline = 'middle'`, and a single `fillText(text, width / 2, height / 2)` call — these two alignment settings are what center the text both horizontally and vertically around the canvas midpoint in one step, rather than requiring separate manual offset calculations for each axis. The output format select (PNG, JPEG or WebP) only affects the final `canvas.toBlob()` export call when you click download; it has no effect on how the canvas itself is drawn, and the downloaded filename automatically includes the actual pixel dimensions, e.g. `placeholder-800x600.png`.