drawImage crop user selected area has incorrect position and scale

I have a Chrome extension that allows the user to take a screenshot from a selected area of their screen.

The flow looks like this:

  1. User clicks the screenshot button in the Chrome Extension popup menu. The popup sends a message to the Background script to capture the tab.
  2. The background script screenshots the entire tab and sends the URL to the content script.
  3. The content script allows the user to draw a rectangle on the screen and it crops out that area of the screen from the screenshot and sends it to the server.

The Issue: When the user selects the area to crop, the final result is scaled in and shifted to the left and up a bit. For the life of me, I can’t figure out why. Also, if I try to crop when the page is scrolled vertically/horizontally the canvas gets created at the top. I’m not sure if that’s a related issue or not.

Example Screenshots
Original Image
Generic number grid
Cropped Result (I selected the entire image when cropping)
Generic number grid when I put the crop container over the entire thing

The code below is a modified version of just the content script, without the server calls.

let selectableCanvasArea: HTMLCanvasElement;
let screenshotUrl: string;
let startX: number, startY: number, endX: number, endY: number;

export const startScreenshotProcess = (screenshotUrlParam: string): void => {
    screenshotUrl = screenshotUrlParam; // This comes from the background script
    createCanvas();
    // Change the cursor
    document.body.style.cursor = "crosshair";
    // Darken the entire screen so we can see the cursor
    document.body.style.filter = "brightness(50%)";
    window.addEventListener("mousedown", mouseDownListener);
    window.addEventListener("mouseup", mouseUpListener);
}

const createCanvas = () => {
    // Create the canvas and prepend it to the HTML
    selectableCanvasArea = document.createElement("canvas");
    selectableCanvasArea.style.position = "absolute";
    selectableCanvasArea.style.top = "0px";
    selectableCanvasArea.style.left = "0px";
    selectableCanvasArea.width = 0;
    selectableCanvasArea.height = 0;
    selectableCanvasArea.style.zIndex = "9999";
    selectableCanvasArea.style.border = "3px dashed lightblue";
    document.body.insertAdjacentElement('beforebegin', selectableCanvasArea);
}

const mouseDownListener = (e: MouseEvent): void => {
    startX = e.clientX;
    startY = e.clientY;
    selectableCanvasArea.style.top = startY + "px";
    selectableCanvasArea.style.left = startX + "px";
    window.addEventListener("mousemove", mouseMoveListener);
}

const mouseMoveListener = (e: MouseEvent): void => {
    e.preventDefault();
    selectableCanvasArea.width = Math.abs(e.clientX - startX);
    selectableCanvasArea.height = Math.abs(e.clientY - startY);
}

const mouseUpListener = (e: MouseEvent): void => {
    endX = e.clientX;
    endY = e.clientY;
    processScreenshot();
}

export const processScreenshot = async () => {
    const image = new Image();
    image.src = screenshotUrl;
    image.onload = () => {
        let pos = selectableCanvasArea.getBoundingClientRect();
        
        let originalX = (pos.left + window.scrollX);
        let originalY = (pos.top + window.scrollY);

        let croppedWidth = pos.width;
        let croppedHeight = pos.height;
        
        let croppedCanvas = document.createElement("canvas");
        croppedCanvas.width = croppedWidth;
        croppedCanvas.height = croppedHeight;

        let ratioX = image.naturalWidth / croppedWidth;
        let ratioY = image.naturalHeight / croppedHeight;
        
        let context = croppedCanvas.getContext("2d");
        
        context?.drawImage(image,
            originalX, originalY, // sx, sy
            croppedWidth, croppedHeight, // sWidth, sHeight
            0, 0, // dx, dy
            croppedWidth, croppedHeight // dWidth, dHeight
        );
        document.body.insertAdjacentHTML('beforebegin', `<img src="${croppedCanvas.toDataURL()}" alt="Cropped Image"/>`);
    }
}