diff --git a/fetch/programmer-humour/index.html b/fetch/programmer-humour/index.html
new file mode 100644
index 000000000..3794041da
--- /dev/null
+++ b/fetch/programmer-humour/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+ Programmer humour
+
+
+
+
+ Progammer humour image below
+
+
+
+
+
\ No newline at end of file
diff --git a/fetch/programmer-humour/script.js b/fetch/programmer-humour/script.js
new file mode 100644
index 000000000..16a90098b
--- /dev/null
+++ b/fetch/programmer-humour/script.js
@@ -0,0 +1,64 @@
+const imageElement = document.querySelector("img");
+const endpoint = `https://xkcd.now.sh/?comic=latest`;
+
+let imageCache = null;
+let imagePromise = null;
+
+const state = {
+ imgData: {},
+};
+
+async function fetchImageData() {
+ if (imageCache) return imageCache;
+ if (!imagePromise) {
+ imagePromise = fetch(endpoint)
+ .then((response) => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then((data) => {
+ imageCache = data;
+ return imageCache;
+ });
+ }
+ return imagePromise;
+}
+
+async function setup() {
+ try {
+ const fetchedImgData = await fetchImageData();
+ state.imgData = fetchedImgData;
+ renderImage();
+ const loadingMessage = document.querySelector(".app-message");
+ if (loadingMessage) {
+ loadingMessage.remove();
+ }
+ } catch (error) {
+ showMessage("Sorry, we could not load the image right now.");
+ }
+}
+
+function renderImage() {
+ if (imageElement && state.imgData.img) {
+ imageElement.src = state.imgData.img;
+ imageElement.alt = state.imgData.alt || "Comic image";
+ }
+}
+
+function showMessage(message, duration = 3000) {
+ const existingMessage = document.querySelector(".app-message");
+ if (existingMessage) existingMessage.remove();
+
+ const messageBox = document.createElement("div");
+ messageBox.className = "app-message";
+ messageBox.textContent = message;
+ document.body.appendChild(messageBox);
+
+ setTimeout(() => {
+ messageBox.remove();
+ }, duration);
+}
+
+window.addEventListener("DOMContentLoaded", setup);
diff --git a/fetch/programmer-humour/style.css b/fetch/programmer-humour/style.css
new file mode 100644
index 000000000..c966dfd97
--- /dev/null
+++ b/fetch/programmer-humour/style.css
@@ -0,0 +1,63 @@
+/* CSS Reset */
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ font-size: 100%;
+ line-height: 1.5;
+}
+
+img {
+ display: block;
+ width: 100%;
+ height: auto;
+}
+
+body {
+ min-height: 100vh;
+ display: grid;
+ place-items: center;
+ font-family: Arial, sans-serif;
+ background: #f4f4f4;
+ color: #222;
+}
+
+main {
+ width: 90%;
+ max-width: 600px;
+}
+
+h1 {
+ text-align: center;
+ font-size: 1em;
+ margin: 1rem 0;
+}
+
+/* shows loading message */
+.app-message {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: #b53a3a;
+ color: white;
+ padding: 0.8rem 1rem;
+ border-radius: 6px;
+ z-index: 1000;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+}
+
+@media (min-width: 600px) {
+ h1 {
+ font-size: 1.8em;
+ }
+}