How do I resolve an asynchronous load issue with JS?

I created a new portfolio project to host on my personal website with Kaboom.js and I’m having some issues getting it to load as intended. I suspect the issue is with the asynchronous nature of loading assets in Kaboom.js but so far I’ve been unsuccessful in resolving it.

The Project:
The visitor to the site walks around a house checking out different objects I’ve placed. Each object has a dialog box that gives information about my skills, resume, and qualifications. Imagine walking around a house in Pokémon Red. That’s the style and gameplay type. I used a sprite sheet and a map for the visuals.

Here is the code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>import { dialogData, scaleFactor } from "./constants";
import { k } from "./kaboomCtx";
import { displayDialog, setCamScale, openContactForm, closeContactForm } from "./utils";
document.addEventListener("DOMContentLoaded", () => {
console.log('import.meta.url:', import.meta.url);
console.log('Spritesheet URL:', new URL('../spritesheet.png', import.meta.url));
k.loadSprite ("spritesheet", new URL ('../spritesheet.png', import.meta.url), {
sliceX: 39,
sliceY: 31,
anims: {
"idle-down": 936,
"walk-down": { from: 936, to: 939, loop: true, speed: 8 },
"idle-side": 975,
"walk-side": { from: 975, to: 978, loop: true, speed: 8 },
"idle-up": 1014,
"walk-up": { from: 1014, to: 1017, loop: true, speed: 8 },
"bag-open": 128,
},
});
console.log('Map sprite URL:', new URL('../map.png', import.meta.url));
k.loadSprite ("map", new URL ('../map.png', import.meta.url));
k.load(() => {
k.setBackground (k.Color.fromHex ("#311047"));
// Creating a scene
k.scene ("main", async () => {
const mapJsonUrl = new URL('../map.json', import.meta.url);
console.log('Map JSON URL:', mapJsonUrl);
const mapData = await (await fetch(mapJsonUrl)).json();
console.log('Map data:', mapData);
const layers = mapData.layers;
console.log('Layers:', layers);
const map = k.add ([
k.sprite ("map"),
k.pos (0, 0),
// Controls the scale size of the map -- see constants.js
k.scale (scaleFactor)
]);
const player = k.make ([
k.sprite ("spritesheet", { anim: "idle-down" }),
k.area ({
shape: new k.Rect (k.vec2(0, 3), 10, 10),
}),
k.body (),
k.anchor ("center"),
k.pos (),
k.scale (scaleFactor),
{
speed: 250,
direction: "down",
isInDialog: false,
},
"player",
]);
for (const layer of layers) {
if (layer.name == "Boundaries") {
for (const boundary of layer.objects) {
map.add ([
k.area ({
shape: new k.Rect (k.vec2(0), boundary.width, boundary.height),
}),
k.body ({isStatic: true}),
k.pos (boundary.x, boundary.y),
boundary.name,
]);
if (boundary.name) {
player.onCollide(boundary.name, () => {
player.isInDialog = true;
displayDialog(
dialogData[boundary.name],
() => (player.isInDialog = false)
);
});
}
}
continue;
}
if (layer.name == "Spawnpoint") {
for (const entity of layer.objects) {
if (entity.name == "player") {
player.pos = k.vec2(
(map.pos.x + entity.x) * scaleFactor,
(map.pos.y + entity.y) * scaleFactor
);
k.add(player);
continue;
}
}
}
}
setCamScale(k)
k.onResize(() => {
setCamScale(k);
});
k.onUpdate(() => {
k.camPos(player.pos.x, player.pos.y + 100);
});
// Player movement
k.onMouseDown((mouseBtn) => {
if (mouseBtn !== "left" || player.isInDialog)
return;
const worldMousePos = k.toWorld(k.mousePos());
player.moveTo(worldMousePos, player.speed);
const mouseAngle = player.pos.angle(worldMousePos);
const lowerBound = 50;
const upperBound = 125;
// Up
if (mouseAngle > lowerBound &&
mouseAngle < upperBound &&
player.curAnim() !== "walk-up") {
player.play("walk-up");
player.direction = "up";
return;
}
// Down
if (mouseAngle < -lowerBound &&
mouseAngle > -upperBound &&
player.curAnim() !== "walk-down") {
player.play("walk-down");
player.direction = "down";
return;
}
// Right
if (Math.abs(mouseAngle) > upperBound) {
player.flipX = false;
if (player.curAnim() !== "walk-side") {
player.play("walk-side");
player.direction = "right";
return;
}
}
// Left
if (Math.abs(mouseAngle) < lowerBound) {
player.flipX = true;
if (player.curAnim() !== "walk-side") {
player.play("walk-side");
player.direction = "left";
return;
}
}
});
// Stops walking animation
k.onMouseRelease(() => {
if (player.direction === "down") {
player.play("idle-down");
return;
}
if (player.direction === "up") {
player.play("idle-up");
return;
}
player.play("idle-side");
});
k.go ("main");
});
});
});
// Wait for the DOM to load before querying elements
window.addEventListener('DOMContentLoaded', (event) => {
// Closes the contact form when the page loads
closeContactForm();
// Gets contact form buttons
const openButton = document.querySelector('.ui-popup-button');
const closeButton = document.querySelector('#closeContactFormButton');
// Check if the elements exist before adding event listeners
if (openButton) {
openButton.addEventListener('click', openContactForm);
} else {
console.error("Element with class '.ui-popup-button' not found");
}
if (closeButton) {
closeButton.addEventListener('click', closeContactForm);
} else {
console.error("Element with id 'closeContactFormButton' not found");
}
});
</code>
<code>import { dialogData, scaleFactor } from "./constants"; import { k } from "./kaboomCtx"; import { displayDialog, setCamScale, openContactForm, closeContactForm } from "./utils"; document.addEventListener("DOMContentLoaded", () => { console.log('import.meta.url:', import.meta.url); console.log('Spritesheet URL:', new URL('../spritesheet.png', import.meta.url)); k.loadSprite ("spritesheet", new URL ('../spritesheet.png', import.meta.url), { sliceX: 39, sliceY: 31, anims: { "idle-down": 936, "walk-down": { from: 936, to: 939, loop: true, speed: 8 }, "idle-side": 975, "walk-side": { from: 975, to: 978, loop: true, speed: 8 }, "idle-up": 1014, "walk-up": { from: 1014, to: 1017, loop: true, speed: 8 }, "bag-open": 128, }, }); console.log('Map sprite URL:', new URL('../map.png', import.meta.url)); k.loadSprite ("map", new URL ('../map.png', import.meta.url)); k.load(() => { k.setBackground (k.Color.fromHex ("#311047")); // Creating a scene k.scene ("main", async () => { const mapJsonUrl = new URL('../map.json', import.meta.url); console.log('Map JSON URL:', mapJsonUrl); const mapData = await (await fetch(mapJsonUrl)).json(); console.log('Map data:', mapData); const layers = mapData.layers; console.log('Layers:', layers); const map = k.add ([ k.sprite ("map"), k.pos (0, 0), // Controls the scale size of the map -- see constants.js k.scale (scaleFactor) ]); const player = k.make ([ k.sprite ("spritesheet", { anim: "idle-down" }), k.area ({ shape: new k.Rect (k.vec2(0, 3), 10, 10), }), k.body (), k.anchor ("center"), k.pos (), k.scale (scaleFactor), { speed: 250, direction: "down", isInDialog: false, }, "player", ]); for (const layer of layers) { if (layer.name == "Boundaries") { for (const boundary of layer.objects) { map.add ([ k.area ({ shape: new k.Rect (k.vec2(0), boundary.width, boundary.height), }), k.body ({isStatic: true}), k.pos (boundary.x, boundary.y), boundary.name, ]); if (boundary.name) { player.onCollide(boundary.name, () => { player.isInDialog = true; displayDialog( dialogData[boundary.name], () => (player.isInDialog = false) ); }); } } continue; } if (layer.name == "Spawnpoint") { for (const entity of layer.objects) { if (entity.name == "player") { player.pos = k.vec2( (map.pos.x + entity.x) * scaleFactor, (map.pos.y + entity.y) * scaleFactor ); k.add(player); continue; } } } } setCamScale(k) k.onResize(() => { setCamScale(k); }); k.onUpdate(() => { k.camPos(player.pos.x, player.pos.y + 100); }); // Player movement k.onMouseDown((mouseBtn) => { if (mouseBtn !== "left" || player.isInDialog) return; const worldMousePos = k.toWorld(k.mousePos()); player.moveTo(worldMousePos, player.speed); const mouseAngle = player.pos.angle(worldMousePos); const lowerBound = 50; const upperBound = 125; // Up if (mouseAngle > lowerBound && mouseAngle < upperBound && player.curAnim() !== "walk-up") { player.play("walk-up"); player.direction = "up"; return; } // Down if (mouseAngle < -lowerBound && mouseAngle > -upperBound && player.curAnim() !== "walk-down") { player.play("walk-down"); player.direction = "down"; return; } // Right if (Math.abs(mouseAngle) > upperBound) { player.flipX = false; if (player.curAnim() !== "walk-side") { player.play("walk-side"); player.direction = "right"; return; } } // Left if (Math.abs(mouseAngle) < lowerBound) { player.flipX = true; if (player.curAnim() !== "walk-side") { player.play("walk-side"); player.direction = "left"; return; } } }); // Stops walking animation k.onMouseRelease(() => { if (player.direction === "down") { player.play("idle-down"); return; } if (player.direction === "up") { player.play("idle-up"); return; } player.play("idle-side"); }); k.go ("main"); }); }); }); // Wait for the DOM to load before querying elements window.addEventListener('DOMContentLoaded', (event) => { // Closes the contact form when the page loads closeContactForm(); // Gets contact form buttons const openButton = document.querySelector('.ui-popup-button'); const closeButton = document.querySelector('#closeContactFormButton'); // Check if the elements exist before adding event listeners if (openButton) { openButton.addEventListener('click', openContactForm); } else { console.error("Element with class '.ui-popup-button' not found"); } if (closeButton) { closeButton.addEventListener('click', closeContactForm); } else { console.error("Element with id 'closeContactFormButton' not found"); } }); </code>
import { dialogData, scaleFactor } from "./constants";
import { k } from "./kaboomCtx";
import { displayDialog, setCamScale, openContactForm, closeContactForm } from "./utils";

document.addEventListener("DOMContentLoaded", () => {

    console.log('import.meta.url:', import.meta.url);

    console.log('Spritesheet URL:', new URL('../spritesheet.png', import.meta.url));
    k.loadSprite ("spritesheet", new URL ('../spritesheet.png', import.meta.url), {

        sliceX: 39,
        sliceY: 31,
        anims: {
            "idle-down": 936,
            "walk-down": { from: 936, to: 939, loop: true, speed: 8 },
            "idle-side": 975,
            "walk-side": { from: 975, to: 978, loop: true, speed: 8 },
            "idle-up": 1014,
            "walk-up": { from: 1014, to: 1017, loop: true, speed: 8 },
            "bag-open": 128,
        },
    });

    console.log('Map sprite URL:', new URL('../map.png', import.meta.url));
    k.loadSprite ("map", new URL ('../map.png', import.meta.url));

    k.load(() => {

        k.setBackground (k.Color.fromHex ("#311047"));

        // Creating a scene
        k.scene ("main", async () => {
            
            const mapJsonUrl = new URL('../map.json', import.meta.url);
            console.log('Map JSON URL:', mapJsonUrl);
            const mapData = await (await fetch(mapJsonUrl)).json();
            console.log('Map data:', mapData);
            const layers = mapData.layers;
            console.log('Layers:', layers);

            const map = k.add ([
                k.sprite ("map"),
                k.pos (0, 0),
                // Controls the scale size of the map -- see constants.js
                k.scale (scaleFactor)
            ]);

            const player = k.make ([
                k.sprite ("spritesheet", { anim: "idle-down" }),
                k.area ({ 
                    shape: new k.Rect (k.vec2(0, 3), 10, 10),
                }),
                k.body (),
                k.anchor ("center"),
                k.pos (),
                k.scale (scaleFactor),
                {
                    speed: 250,
                    direction: "down",
                    isInDialog: false,
                },
                "player",
            ]);

            for (const layer of layers) {
                if (layer.name == "Boundaries") {
                    for (const boundary of layer.objects) {
                        map.add ([
                            k.area ({
                                shape: new k.Rect (k.vec2(0), boundary.width, boundary.height),
                            }),
                            k.body ({isStatic: true}),
                            k.pos (boundary.x, boundary.y),
                            boundary.name,
                        ]);

                        if (boundary.name) {
                            player.onCollide(boundary.name, () => {
                                player.isInDialog = true;

                                displayDialog(
                                    dialogData[boundary.name],
                                    () => (player.isInDialog = false)
                                );
                            });
                        }
                    }
                    
                    continue;
                }

                if (layer.name == "Spawnpoint") {
                    for (const entity of layer.objects) {
                        if (entity.name == "player") {
                            player.pos = k.vec2(
                                (map.pos.x + entity.x) * scaleFactor,
                                (map.pos.y + entity.y) * scaleFactor
                            );
                            k.add(player);
                            continue;
                        }
                    }
                }
            }

            setCamScale(k)

            k.onResize(() => {
                setCamScale(k);
            });

            k.onUpdate(() => {
                k.camPos(player.pos.x, player.pos.y + 100);
            });

            // Player movement
            k.onMouseDown((mouseBtn) => {
                if (mouseBtn !== "left" || player.isInDialog)
                    return;

                const worldMousePos = k.toWorld(k.mousePos());
                player.moveTo(worldMousePos, player.speed);

                const mouseAngle = player.pos.angle(worldMousePos);
                const lowerBound = 50;
                const upperBound = 125;

                // Up
                if (mouseAngle > lowerBound && 
                    mouseAngle < upperBound && 
                    player.curAnim() !== "walk-up") {

                    player.play("walk-up");
                    player.direction = "up";
                    return;
                }

                // Down
                if (mouseAngle < -lowerBound &&
                    mouseAngle > -upperBound &&
                    player.curAnim() !== "walk-down") {

                    player.play("walk-down");
                    player.direction = "down";
                    return;
                    }

                // Right
                if (Math.abs(mouseAngle) > upperBound) {
                    player.flipX = false;

                    if (player.curAnim() !== "walk-side") {
                        player.play("walk-side");
                        player.direction = "right";
                        return;
                    }
                }

                // Left
                if (Math.abs(mouseAngle) < lowerBound) {
                    player.flipX = true;

                    if (player.curAnim() !== "walk-side") {
                        player.play("walk-side");
                        player.direction = "left";
                        return;
                    }
                }
            });

            // Stops walking animation
            k.onMouseRelease(() => {
                if (player.direction === "down") {
                    player.play("idle-down");
                    return;
                }

                if (player.direction === "up") {
                    player.play("idle-up");
                    return;
                }

                player.play("idle-side");
            });

            k.go ("main");
        });
    });
});

// Wait for the DOM to load before querying elements
window.addEventListener('DOMContentLoaded', (event) => {
    
    // Closes the contact form when the page loads
    closeContactForm();
    
    // Gets contact form buttons
    const openButton = document.querySelector('.ui-popup-button');
    const closeButton = document.querySelector('#closeContactFormButton');

    // Check if the elements exist before adding event listeners
    if (openButton) {
        openButton.addEventListener('click', openContactForm);
    } else {
        console.error("Element with class '.ui-popup-button' not found");
    }

    if (closeButton) {
        closeButton.addEventListener('click', closeContactForm);
    } else {
        console.error("Element with id 'closeContactFormButton' not found");
    }
});

And here is the error from the Chrome console:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'.
at b.add (index-DfV-Wg4v.js:1:51115)
at SpriteData.fromImage (index-DfV-Wg4v.js:1:66115)
at Object.loadSprite (index-DfV-Wg4v.js:1:69732)
at HTMLDocument.<anonymous> (index-DfV-Wg4v.js:1:136855)
</code>
<code>Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'. at b.add (index-DfV-Wg4v.js:1:51115) at SpriteData.fromImage (index-DfV-Wg4v.js:1:66115) at Object.loadSprite (index-DfV-Wg4v.js:1:69732) at HTMLDocument.<anonymous> (index-DfV-Wg4v.js:1:136855) </code>
Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'.
    at b.add (index-DfV-Wg4v.js:1:51115)
    at SpriteData.fromImage (index-DfV-Wg4v.js:1:66115)
    at Object.loadSprite (index-DfV-Wg4v.js:1:69732)
    at HTMLDocument.<anonymous> (index-DfV-Wg4v.js:1:136855)

A few things to note:

  1. The contact form functionality works. It’s the map and sprites that won’t show.

  2. As you can see in the code, I’ve added console.log code to check the status of certain variables. Only import.meta.url and spritesheet url fire in the console.

  3. I’ve tried window.onload and defer.

  4. If I take the assets and plug my website name into the path they load in the browser so I know the issue isn’t there.

  5. It did work fine testing locally but as usual, it’s a different ballgame when it comes to hosting.

New contributor

glitchedb0t is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật