Why does drawImage’s scaling add borders?

I’m not so sure if “borders” is the right term, but anyway:
My problem is that if I try to (up)scale a tile from a tile atlas, it adds parts of the tiles next to it.

My tile drawing code is as follows:

ctx.drawImage(tileAtlas, s.x, s.y, s.w, s.h, d.x, d.y, d.w, d.h);

s is the source and d the destination. The calculation for the destination’s width and height is the source’s multiplied by the scalar.

const d = {
            ...
            w: s.w *scalarX,
            h: s.h *scalarY
}

The end result for scaling by two look like this:

canvas scaled by two

The tileset:

the tileset

The lines appear for every tile that is (up)scaled and for every upscaling I tried (1.25, 1.5, 2, 5, 10, …) only 1, 0.75, 0.625, 0.5, 0.375, 0.25 and so on worked.

The size of the tiles is not the problem, here these are 64×64.
I also checked if the wrong positions have been given to s or d but they align to the upscaled tilesize of 128×128 that the destination shall have.

Here as a snipped, I scaled down the code as much as possible.

const canvasElement = document.getElementById("canvas");
const ctx = canvasElement.getContext("2d");

const tileAtlas = new Image();
tileAtlas.src = "https://i.stack.imgur.com/PdcQg.png"; //path to the tile atlas file; here the img in the post

const tiles = {
    width_x: 64,
    height_y: 64,
    scalar: 1
}
const canv = {
    width_x: 4, //size in tiles
    height_y: 4
}

/* applies canv to the canvas and clears it */
function applyCanvas() {
    const sizeX = canv.width_x * tiles.width_x * tiles.scalar;
    const sizeY = canv.height_y * tiles.height_y * tiles.scalar;
    canvasElement.setAttribute("width", sizeX);
    canvasElement.setAttribute("height", sizeY);
    ctx.fillStyle = "#ffffff";
    ctx.fillRect(0, 0, sizeX, sizeY);
}

/* draws the tile in tile atlas at tx, ty
 * to the canvas at cx, cy
 * the canvas cordinates are based on the tilesize
 */
function drawTile(tx=0, ty=0, cx=0, cy=0) {
    const s = {
        x: tx *tiles.width_x,
        y: ty *tiles.height_y,
        w: tiles.width_x,
        h: tiles.height_y
    };
    const d = {
        x: cx *s.w *tiles.scalar,
        y: cy *s.h *tiles.scalar,
        w: s.w *tiles.scalar,
        h: s.h *tiles.scalar
    };
    ctx.drawImage(tileAtlas, s.x, s.y, s.w, s.h, d.x, d.y, d.w, d.h);
}

/* draws the canvas full of the tile at tx, ty
 * and changes the scaling
 */
function fillWithScaler(tx=0, ty=0, scalar=1) {
    tiles.scalar = scalar;
    applyCanvas();
    for (var y = 0; y < canv.height_y; y++) {
        for (var x = 0; x < canv.width_x; x++) {
            drawTile(tx, ty, x, y);
        }
    }
}
<!DOCTYPE html>
<html>
    <body>
        Scaler: <input type="number" value="1" min="0" onchange="fillWithScaler(0, 3, this.value)"> <br>
        <canvas id="canvas" width="256" height="256" style="border: 1px solid gray;"></canvas>
    </body>
    <script src='test.js'></script>
</html>