Image Object & Loading Get Certified
JavaScript lets you create and load images dynamically with the Image constructor. This is useful for galleries, preloading, fallbacks, and any situation where you need to control when an image starts downloading and what happens when it finishes or fails. Click Test My Code on any example to see the text output and the picture it produces, and Code Explanation to walk through the code step by step.
Creating and Loading an Image
new Image() creates an image element in memory. Setting its .src starts the download. The onload and onerror events tell you when it succeeds or fails. In this lesson the examples use a small stand-in called createMockImage() (placed at the bottom of each example) so everything runs without internet and the picture can be drawn in the output; on your own page you simply write new Image().
What this code does: Creates an image object, attaches the onload and onerror handlers, and only then sets src to start loading. When the simulated download succeeds, onload prints the image’s real size and draws the picture below the text output. The order of the steps (handlers first, src last) is the main point of this example.
// On a real page you would write: const img = new Image();
const img = createMockImage(320, 180);
// 1) Attach the handlers FIRST
img.onload = function () {
console.log("Image loaded successfully!");
console.log("Size:", img.naturalWidth + "x" + img.naturalHeight);
showCanvas("The loaded image", img.picture);
};
img.onerror = function () {
console.log("Image failed to load.");
};
// 2) Setting src starts the download
console.log("Starting download...");
img.src = "https://example.com/photo.jpg";
// ===== Helpers below are not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
function createMockImage(width = 320, height = 180, fail = false) {
const img = {
onload: null,
onerror: null,
complete: false,
naturalWidth: 0,
naturalHeight: 0,
picture: null,
set src(url) {
if (fail) {
if (img.onerror) img.onerror(new Error("404 Not Found: " + url));
return;
}
const c = document.createElement("canvas");
c.width = width;
c.height = height;
const g = c.getContext("2d");
const hue = (url.length * 37) % 360;
g.fillStyle = `hsl(${hue}, 55%, 35%)`;
g.fillRect(0, 0, width, height);
g.fillStyle = `hsl(${hue + 40}, 80%, 65%)`;
g.beginPath();
g.arc(width / 2, height / 2, height / 3, 0, Math.PI * 2);
g.fill();
img.picture = c;
img.naturalWidth = width;
img.naturalHeight = height;
img.complete = true;
if (img.onload) img.onload();
}
};
return img;
}
Using a Fallback Image
If the primary image fails, you can quietly switch to a backup image instead of showing a broken-image icon.
What this code does: Tries to load a primary image that is set up to fail (a 404). Its onerror handler logs the problem and then creates a second image object for the fallback URL, which loads successfully and is drawn below the text output. This is the standard pattern for avatars, product photos and any image that may be missing.
function loadWithFallback(primaryUrl, fallbackUrl) {
const img = createMockImage(320, 180, true); // true = this one will fail
img.onload = () => {
console.log("Primary image loaded successfully");
};
img.onerror = (err) => {
console.log("Primary image failed:", err.message);
console.log("Trying fallback image instead...");
const fallback = createMockImage(320, 180, false);
fallback.onload = () => {
console.log("Fallback image loaded successfully!");
showCanvas("Fallback image shown instead", fallback.picture);
};
fallback.src = fallbackUrl;
};
img.src = primaryUrl;
}
loadWithFallback(
"https://example.com/this-image-does-not-exist.jpg",
"https://example.com/fallback.jpg"
);
// ===== Helpers below are not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
function createMockImage(width = 320, height = 180, fail = false) {
const img = {
onload: null,
onerror: null,
complete: false,
naturalWidth: 0,
naturalHeight: 0,
picture: null,
set src(url) {
if (fail) {
if (img.onerror) img.onerror(new Error("404 Not Found: " + url));
return;
}
const c = document.createElement("canvas");
c.width = width;
c.height = height;
const g = c.getContext("2d");
const hue = (url.length * 37) % 360;
g.fillStyle = `hsl(${hue}, 55%, 35%)`;
g.fillRect(0, 0, width, height);
g.fillStyle = `hsl(${hue + 40}, 80%, 65%)`;
g.beginPath();
g.arc(width / 2, height / 2, height / 3, 0, Math.PI * 2);
g.fill();
img.picture = c;
img.naturalWidth = width;
img.naturalHeight = height;
img.complete = true;
if (img.onload) img.onload();
}
};
return img;
}
Preloading Images Before Showing Them
Preloading downloads an image into memory before you need to display it, so it can appear instantly later. In real code this is usually wrapped in a Promise so you can wait for it, but the idea is the same.
What this code does: Keeps a cache (a plain object) of images that are already downloaded. showImage() first reports a miss because nothing is preloaded yet, then preloadImage() downloads the image and stores it in the cache, and the second showImage() finds it and displays it immediately. The picture is only drawn once, at the final step.
// A cache remembers images that are already downloaded
const cache = {};
function preloadImage(url) {
const img = createMockImage(320, 180);
img.onload = () => {
cache[url] = img;
console.log("Preloaded and cached:", url);
};
img.src = url;
}
function showImage(url) {
if (cache[url]) {
console.log("Cache hit: showing instantly:", url);
showCanvas("Shown from the cache", cache[url].picture);
} else {
console.log("Not cached yet: would have to download first");
}
}
showImage("https://example.com/starry-night.jpg"); // not preloaded
preloadImage("https://example.com/starry-night.jpg"); // preload in background
showImage("https://example.com/starry-night.jpg"); // now instant
// ===== Helpers below are not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
function createMockImage(width = 320, height = 180, fail = false) {
const img = {
onload: null,
onerror: null,
complete: false,
naturalWidth: 0,
naturalHeight: 0,
picture: null,
set src(url) {
if (fail) {
if (img.onerror) img.onerror(new Error("404 Not Found: " + url));
return;
}
const c = document.createElement("canvas");
c.width = width;
c.height = height;
const g = c.getContext("2d");
const hue = (url.length * 37) % 360;
g.fillStyle = `hsl(${hue}, 55%, 35%)`;
g.fillRect(0, 0, width, height);
g.fillStyle = `hsl(${hue + 40}, 80%, 65%)`;
g.beginPath();
g.arc(width / 2, height / 2, height / 3, 0, Math.PI * 2);
g.fill();
img.picture = c;
img.naturalWidth = width;
img.naturalHeight = height;
img.complete = true;
if (img.onload) img.onload();
}
};
return img;
}
Reading Image Properties
Once an image has loaded, it exposes useful properties such as naturalWidth, naturalHeight, and complete.
What this code does: Creates a 1920×1080 image, prints its properties before loading (complete is false, size is 0x0 in a real browser), and again after loading. Inside onload it calculates the aspect ratio, works out a smaller display size that keeps the same proportions, and draws the image scaled down. This shows the difference between the image’s natural size and the size you display it at.
const img = createMockImage(1920, 1080);
console.log("Before loading: complete:", img.complete,
"| size:", img.naturalWidth + "x" + img.naturalHeight);
img.onload = function () {
console.log("After loading: complete:", img.complete);
console.log("Real pixel dimensions:", img.naturalWidth + "x" + img.naturalHeight);
const aspectRatio = (img.naturalWidth / img.naturalHeight).toFixed(2);
console.log("Aspect ratio:", aspectRatio, "(16:9 is about 1.78)");
// Display it smaller than its natural size
const displayWidth = 320;
const displayHeight = Math.round(displayWidth / (img.naturalWidth / img.naturalHeight));
console.log("Displayed size:", displayWidth + "x" + displayHeight);
const small = document.createElement("canvas");
small.width = displayWidth;
small.height = displayHeight;
small.getContext("2d").drawImage(img.picture, 0, 0, displayWidth, displayHeight);
showCanvas("Displayed at 320x180 (natural size is 1920x1080)", small);
};
img.src = "https://example.com/photo.jpg";
// ===== Helpers below are not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
function createMockImage(width = 320, height = 180, fail = false) {
const img = {
onload: null,
onerror: null,
complete: false,
naturalWidth: 0,
naturalHeight: 0,
picture: null,
set src(url) {
if (fail) {
if (img.onerror) img.onerror(new Error("404 Not Found: " + url));
return;
}
const c = document.createElement("canvas");
c.width = width;
c.height = height;
const g = c.getContext("2d");
const hue = (url.length * 37) % 360;
g.fillStyle = `hsl(${hue}, 55%, 35%)`;
g.fillRect(0, 0, width, height);
g.fillStyle = `hsl(${hue + 40}, 80%, 65%)`;
g.beginPath();
g.arc(width / 2, height / 2, height / 3, 0, Math.PI * 2);
g.fill();
img.picture = c;
img.naturalWidth = width;
img.naturalHeight = height;
img.complete = true;
if (img.onload) img.onload();
}
};
return img;
}
Loading an Image Only When It’s Needed
Lazy loading delays an image’s download until it is actually about to be seen, saving bandwidth on long pages. In real pages, the visibility check is done with the IntersectionObserver API or the loading="lazy" attribute.
What this code does: Defines lazyLoad(), which does nothing while the image is not visible and only creates and loads the image once it is. The example calls it twice: first with ‘not visible’ (no download), then after a simulated scroll with ‘visible’ (download and picture). The text output shows the order of events.
function lazyLoad(url, isInViewport) {
if (!isInViewport) {
console.log("Not visible yet: skipping load to save bandwidth");
return;
}
console.log("Element scrolled into view: loading now...");
const img = createMockImage(320, 180);
img.onload = () => {
console.log("Loaded just in time!");
showCanvas("Loaded when it scrolled into view", img.picture);
};
img.src = url;
}
// Image is far below the fold
lazyLoad("https://example.com/pillars.jpg", false);
console.log("--- user scrolls down ---");
// Now it is in view
lazyLoad("https://example.com/pillars.jpg", true);
// ===== Helpers below are not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
function createMockImage(width = 320, height = 180, fail = false) {
const img = {
onload: null,
onerror: null,
complete: false,
naturalWidth: 0,
naturalHeight: 0,
picture: null,
set src(url) {
if (fail) {
if (img.onerror) img.onerror(new Error("404 Not Found: " + url));
return;
}
const c = document.createElement("canvas");
c.width = width;
c.height = height;
const g = c.getContext("2d");
const hue = (url.length * 37) % 360;
g.fillStyle = `hsl(${hue}, 55%, 35%)`;
g.fillRect(0, 0, width, height);
g.fillStyle = `hsl(${hue + 40}, 80%, 65%)`;
g.beginPath();
g.arc(width / 2, height / 2, height / 3, 0, Math.PI * 2);
g.fill();
img.picture = c;
img.naturalWidth = width;
img.naturalHeight = height;
img.complete = true;
if (img.onload) img.onload();
}
};
return img;
}
Quick Reference: Image Properties and Events
| Property / Event | Purpose |
|---|---|
new Image() | Creates an image element in memory |
.src | Setting this starts the download |
onload | Fires once the image has fully downloaded |
onerror | Fires if the image fails to load |
naturalWidth / naturalHeight | The image’s real, unscaled pixel dimensions |
complete | true once loading has finished |
Common Mistakes
- Setting
.srcbefore attachingonload/onerror - Never adding an
onerrorhandler - Reading
naturalWidth/naturalHeightbefore the image has loaded, or confusing them with the displayed size - Assuming the image is ready on the line after
.src(real images load asynchronously) - Loading every image on a long page immediately instead of lazy-loading
- Preloading right before displaying an image (which defeats the purpose)
Best Practices
- Always pair
.srcwith anonerrorhandler - Preload only images the user is likely to need soon
- Use the native
loading="lazy"attribute when possible - Wrap image loading in a
Promisefor clean coordination - Reserve space for images so the page doesn’t jump when they arrive
Quick Summary
new Image()creates an image in memory; setting.srcstarts the downloadonloadfires on success;onerrorfires on failure, so always handle both- Preloading downloads an image ahead of time so it appears instantly later
naturalWidth/naturalHeightgive real pixel dimensions- Lazy loading delays a load until the image is about to be seen
Try It Yourself
What this code does: Loads a small gallery of three images, one of which is set up to fail. Each image gets its own onload and onerror handlers, successful pictures are displayed, failures are counted and reported, and a summary is printed. It combines creating images, attaching handlers before src, and handling both outcomes. Try changing a fail value or adding another item to the gallery.
const gallery = [
{ caption: "Bald Eagle", url: "https://example.com/eagle.jpg", fail: false },
{ caption: "Missing Photo", url: "https://example.com/missing.jpg", fail: true },
{ caption: "Mountain Lake", url: "https://example.com/lake.jpg", fail: false }
];
let loaded = 0;
let failed = 0;
gallery.forEach((item) => {
const img = createMockImage(240, 140, item.fail);
img.onload = () => {
loaded++;
console.log("Loaded:", item.caption);
showCanvas(item.caption, img.picture);
};
img.onerror = (err) => {
failed++;
console.log("Failed:", item.caption, "-", err.message);
};
img.src = item.url;
});
console.log("Summary:", loaded, "loaded,", failed, "failed");
// ===== Helpers below are not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
function createMockImage(width = 320, height = 180, fail = false) {
const img = {
onload: null,
onerror: null,
complete: false,
naturalWidth: 0,
naturalHeight: 0,
picture: null,
set src(url) {
if (fail) {
if (img.onerror) img.onerror(new Error("404 Not Found: " + url));
return;
}
const c = document.createElement("canvas");
c.width = width;
c.height = height;
const g = c.getContext("2d");
const hue = (url.length * 37) % 360;
g.fillStyle = `hsl(${hue}, 55%, 35%)`;
g.fillRect(0, 0, width, height);
g.fillStyle = `hsl(${hue + 40}, 80%, 65%)`;
g.beginPath();
g.arc(width / 2, height / 2, height / 3, 0, Math.PI * 2);
g.fill();
img.picture = c;
img.naturalWidth = width;
img.naturalHeight = height;
img.complete = true;
if (img.onload) img.onload();
}
};
return img;
}
Tip: Attachonloadandonerrorbefore you ever set.src. Setting the source is what starts the request, so your handlers need to already be in place.
🧪 Test Your JavaScript Code
Edit the code on the left and click “Run Code” to see the console output on the right.
Click “Run Code” to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.