Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallSome links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Create a new destination <canvas>, set its bitmap dimensions, and draw each source canvas onto it with drawImage(). You can place canvases side by side, stack them, overlap them, scale them, or crop them. When composition is complete, export the destination canvas with toBlob() or, when you specifically need a data URL, toDataURL().
Combine canvases with a destination canvas
An HTMLCanvasElement can be the source for CanvasRenderingContext2D.drawImage(); you do not need to export each source to an intermediate image first. Set the output canvas’s intrinsic width and height to the size of the final bitmap, then draw each source at the coordinates where it belongs. See MDN’s guide to using images and canvases and the drawImage() reference.
Runnable composition example
This function accepts source canvases and destination positions. If an item has destination dimensions, it scales that canvas; otherwise, it draws the source at its intrinsic bitmap size.
function combineCanvases(items, outputWidth, outputHeight) {
const output = document.createElement("canvas");
output.width = outputWidth;
output.height = outputHeight;
const context = output.getContext("2d");
if (!context) {
throw new Error("2D canvas context is unavailable");
}
for (const item of items) {
const { canvas, x, y, width, height } = item;
if (!(canvas instanceof HTMLCanvasElement)) {
throw new TypeError("Each item must contain an HTMLCanvasElement");
}
if (width == null || height == null) {
context.drawImage(canvas, x, y);
} else {
context.drawImage(canvas, x, y, width, height);
}
}
return output;
}
const first = document.querySelector("#first");
const second = document.querySelector("#second");
if (!first || !second) {
throw new Error("Could not find both source canvases");
}
const combined = combineCanvases([
{ canvas: first, x: 0, y: 0 },
{ canvas: second, x: first.width, y: 0 }
], first.width + second.width, Math.max(first.height, second.height));
For this side-by-side layout, the second canvas starts at the first canvas’s bitmap width. The output is wide enough for both and tall enough for the taller source. If you want a gap, add it to the second canvas’s x coordinate and the output width. For a vertical stack, use the first canvas’s height as the second canvas’s y, and add the heights to calculate the output height.
#1 Best Overall
Set bitmap dimensions, not just CSS dimensions
The output’s width and height properties determine its bitmap dimensions. CSS sizing only changes how the canvas is displayed and can stretch or shrink its appearance without changing the bitmap you export. Choose output dimensions in pixels for the image you actually need. If the destination is smaller than the drawn content, pixels outside its bounds are clipped.
Choose placement, scaling, and cropping
The drawImage() overload you choose determines whether a source is drawn at its intrinsic dimensions, scaled to a destination rectangle, or cropped from a source rectangle before drawing. The API’s documented overloads are described in the MDN reference.
Scale a source to fit a destination rectangle
Pass destination width and height to scale the whole source canvas into a chosen rectangle:
context.drawImage(sourceCanvas, x, y, destinationWidth, destinationHeight);
This scales to the rectangle’s exact proportions. If the source and destination aspect ratios differ, the image can look stretched. To preserve proportions, calculate one dimension from the other using the source’s width-to-height ratio, and leave any remaining space as a margin or background.
Rank #2
Crop part of a source canvas
Use the source-rectangle overload when only part of a canvas belongs in the composition:
// source x, source y, source width, source height,
// destination x, destination y, destination width, destination height
context.drawImage(sourceCanvas, 20, 10, 200, 120, 0, 0, 200, 120);
The source rectangle selects pixels from the original canvas; the destination rectangle sets their position and size in the output. Make sure the source and destination rectangles fall where you intend. Drawing outside the output bitmap is clipped.
Control overlap and background
Canvas draws in sequence: later draws appear over earlier ones where they overlap. A new canvas starts transparent. If you need a solid background, paint it before drawing sources:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
context.fillStyle = "white";
context.fillRect(0, 0, output.width, output.height);
Put this before the loop. To preserve transparency, omit the fill. The order of the items in the array is therefore significant for overlapping compositions.
Wait until source canvases are ready
Composition only captures pixels that have already been painted. If a source canvas is drawn from images, wait for those image loads and for your drawing code to finish before calling combineCanvases(). Otherwise, the combined output may contain blank or incomplete regions. MDN explains coordinating image loading in its Using images tutorial.
For example, if you control the source rendering, call the composition function after that rendering completes rather than immediately after starting asynchronous work. If the source images may fail to load, handle those failures too; an unresolved or failed image should not be treated as ready content.
Export the combined canvas
Download with toBlob()
For a downloadable image or further processing, toBlob() avoids building a large base64 data URL string. It returns a Blob through a callback, or null if the image cannot be created. This example downloads PNG and releases the object URL after the browser has had time to start the download:
function downloadCanvas(canvas, filename = "combined.png") {
canvas.toBlob((blob) => {
if (!blob) {
throw new Error("Canvas image could not be created");
}
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
// Keep the URL alive for the download, then release its memory.
setTimeout(() => URL.revokeObjectURL(url), 1000);
}, "image/png");
}
downloadCanvas(combined);
PNG is the default format if the type is omitted, and is also the fallback if the requested type is unsupported. Supported lossy formats may accept a quality value from 0 to 1. The MDN toBlob() reference covers the callback and format behavior.
Rank #4
Use a data URL only when you need one
Use toDataURL() if the next step specifically requires a data URL and the image is a suitable size:
const dataUrl = combined.toDataURL("image/png");
Large data URLs can consume memory and may run into URL-length limitations when assigned to an image’s src. MDN recommends considering toBlob() and an object URL for larger images; see the toDataURL() reference. A zero-sized canvas or a canvas beyond that browser’s maximum supported size returns "data:,"; maximum canvas dimensions vary by browser and device, so there is no single safe universal pixel ceiling.
Pick an image format for the job
- PNG: A lossless choice when sharp edges, text, or transparency matter.
- A supported lossy format: Consider it when smaller output matters more than lossless fidelity; supply a quality value where the browser supports it.
Actual format support and encoded size depend on the browser and content. Check the resulting Blob’s type if downstream code relies on a particular format rather than assuming every requested type is supported.
Recommended Free Tools
Understand CORS and the tainted-canvas error
If you draw pixels from an image hosted on another origin without the required CORS approval, the source canvas becomes tainted. Exporting it with toBlob() or toDataURL(), or reading pixels, can then fail with a SecurityError. Drawing a tainted source canvas onto a new destination does not remove the restriction: the destination is tainted too.
Best Value
When the image host permits cross-origin access, set the image’s crossOrigin property before assigning its URL, and the server must return an appropriate Access-Control-Allow-Origin response. For example:
const image = new Image();
image.crossOrigin = "anonymous";
image.onload = () => {
const context = sourceCanvas.getContext("2d");
context.drawImage(image, 0, 0);
};
image.src = "https://images.example/image.png";
Changing the client-side setting alone cannot grant access if the remote server does not allow it. See MDN’s CORS-enabled images guide for the server-side requirement and origin-clean restrictions.
Troubleshoot missing or failed output
- The output is blank or incomplete: Confirm each source exists, has been painted, and is not being composed before its images or other asynchronous content are ready.
- Part of a canvas is missing: Check the output dimensions and each source’s destination coordinates. Pixels beyond the output bounds are clipped.
- A source looks distorted: The destination rectangle may have a different aspect ratio. Calculate the destination dimensions proportionally or use the intrinsic-size overload.
- Export throws
SecurityError: Check whether a source contains foreign-origin image pixels. The host must permit CORS; drawing the source into another canvas does not fix taint. toBlob()returnsnull: Treat this as an export failure and check that the canvas is valid and within browser/device canvas limits.toDataURL()returns"data:,": Check whether the bitmap has zero width or height or exceeds the browser’s maximum canvas size.- The output is unexpectedly large or slow to handle: Avoid converting a large image to a data URL unless required. Prefer a Blob and object URL for downloads or binary processing.
Or skip the browser setup
If what you need is a screenshot of a webpage rather than a composition of canvases already in your app, ScreenshotNeo can capture the page through one API request. It does not merge existing canvas elements into a custom layout; use the JavaScript method above for that task. See the ScreenshotNeo API documentation for request options.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo accepts cookie or consent banners before capture and removes 60+ known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month with no card.
FAQ
Do I need to convert each canvas to an image first?
No. You can pass an HTMLCanvasElement directly to drawImage(); exporting intermediates is unnecessary for composition.
Can I put the same source canvas into the output more than once?
Yes. Call drawImage() repeatedly with different destination coordinates or sizes. The call order determines which drawing appears on top where regions overlap.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →

