Out of curiosity, I tried to create the slideshow.html with the support of PERPLEXITY.AI. The script shows the JPG images in a local directory and reacts to commands sent from an ESP32 with ANNEX.
This example code responds with just an example sequence at http://<IP-ADR>/msg?x=1 or at HTTP://192.168.4.1/msg?x=1 if the ESP is in AP mode. However, it should be very easy to make it send commands according to local push buttons being pressed on the ESP32. I was just a bit too lazy to set that up, too

This is the SLIDESHOW.HTLM for a Chrome or Edge browser on the PC.
The script has to be stored locally on the PC to gain the necessary local file permissions.
It expects a local directory with foto001.jpg, foto002.jpg .... foto999.jpg
The IP-Address of the remote controlling ESP32 , the image directory and the number of files to show are set as constants in the code.
Please insert your values.
I did not find an easy way to examine the local directory for the actual original file names of my photos. So I took the way of renaming the image files by bulk-rename tool.
Use F11-key for fullscreen mode of the slideshow window.
Code: [Local Link Removed for Guests]
<!DOCTYPE html>
<html>
<head>
<title>Slideshow</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: black;
}
img {
position: absolute;
width: 100%;
height: 100%;
object-fit: contain;
transition: opacity 2s;
}
</style>
</head>
<body>
<script>
// Check if the browser is Chrome or Edge
const userAgent = navigator.userAgent;
const isChromeOrEdge = userAgent.includes("Chrome") || userAgent.includes("Edg");
// Display a warning if the browser is not Chrome or Edge
if (!isChromeOrEdge) {
alert("Warning: This application works best in Google Chrome or Microsoft Edge. Please switch to one of these browsers for optimal performance.");
}
const IMAGE_DIR = "C:/Users/pneuf/Pictures/XY/"; // Local directory for images
const IMAGE_PREFIX = "foto"; // Prefix for image filenames
const TOTAL_IMAGES = 60; // Total number of images
// const SERVER_IP = "192.168.0.111"; // Server IP address
const SERVER_IP = "192.168.0.145"; // Server IP address
const CHECK_URL = `http://${SERVER_IP}/msg?x=1`; // URL to query for commands
const COMMANDS = ["next", "previous", "repeat", "go", "stop"]; // Supported commands
const CHANGE_INTERVAL = 30000; // Interval for changing slides (in milliseconds, 30 seconds)
let currentIndex = 0; // Current image index (0-based)
let interval;
// Function to generate a list of image file names
function generateImageList() {
const images = [];
for (let i = 1; i <= TOTAL_IMAGES; i++) {
const paddedNumber = String(i).padStart(3, '0'); // Add leading zeros
images.push(`${IMAGE_PREFIX}${paddedNumber}.jpg`);
}
return images;
}
const images = generateImageList();
// Function to change the displayed image
function changeImage(index) {
const imgElement = document.createElement("img");
imgElement.src = `${IMAGE_DIR}${images[index]}`;
imgElement.style.opacity = 0;
document.body.appendChild(imgElement);
setTimeout(() => {
imgElement.style.opacity = 1;
const oldImages = document.querySelectorAll("img");
if (oldImages.length > 1) {
document.body.removeChild(oldImages[0]);
}
}, 50);
}
// Function to start the slideshow
function startSlideshow() {
interval = setInterval(() => {
currentIndex++;
if (currentIndex >= images.length) {
currentIndex = 0; // Restart with the first image when reaching the upper limit
}
changeImage(currentIndex);
}, CHANGE_INTERVAL); // Change image every 10 seconds
}
// Function to handle commands from the server or keyboard
function handleCommand(command) {
clearInterval(interval);
if (!isNaN(command)) {
// If the command is a number, show the corresponding image
const requestedIndex = parseInt(command, 10) - 1; // Convert to zero-based index
if (requestedIndex >= 0 && requestedIndex < images.length) {
currentIndex = requestedIndex;
changeImage(currentIndex);
}
} else if (command === "next") {
currentIndex++;
if (currentIndex >= images.length) {
currentIndex = 0; // Restart with the first image when reaching the upper limit
}
changeImage(currentIndex);
} else if (command === "previous") {
currentIndex--;
if (currentIndex < 0) {
currentIndex = 0; // Stay at index 0 when trying to go below it
}
changeImage(currentIndex);
} else if (command === "repeat") {
changeImage(currentIndex); // Repeat the current image
} else if (command === "go") {
currentIndex = 0; // Go to the first image and restart slideshow
changeImage(currentIndex);
startSlideshow();
return;
} else if (command === "stop") {
clearInterval(interval); // Stop the slideshow
return; // Exit without restarting slideshow
}
startSlideshow();
}
// Function to periodically query the server for commands
function checkServer() {
setInterval(() => {
fetch(CHECK_URL)
.then(response => response.text())
.then(text => {
text = text.trim(); // Remove any extra whitespace or newline characters
if (!isNaN(text)) {
handleCommand(text); // Handle numeric input as an image index
} else {
for (const command of COMMANDS) {
if (text.includes(command)) {
handleCommand(command);
break;
}
}
}
})
.catch(err => console.error("Error fetching URL:", err));
}, 1000); // Check every 1 second
}
// Function to add keyboard event listeners for local control using arrow keys and p/n keys
function addKeyboardControls() {
document.addEventListener("keydown", (event) => {
switch (event.key) {
case 'ArrowRight': // Next image using right arrow key
case 'n': // Next image using 'n' key
handleCommand("next");
break;
case 'ArrowLeft': // Previous image using left arrow key
case 'p': // Previous image using 'p' key
handleCommand("previous");
break;
case 'r': // Repeat current image using 'r' key
handleCommand("repeat");
break;
case 'g': // Restart slideshow from first image and go using 'g' key
handleCommand("go");
break;
case 's': // Stop slideshow using 's' key
handleCommand("stop");
break;
default:
break; // Ignore other keys
}
});
}
// Initialization
changeImage(currentIndex);
startSlideshow();
checkServer();
addKeyboardControls(); // Add keyboard controls for local navigation using arrow keys and p/n keys
</script>
</body>
</html>
THIS IS THE EXAMPLE ANNEX32-CODE FOR AN ESP32:
Code: [Local Link Removed for Guests]
' REMOTE.BAS a remotecontrol for a browser based slideshow
' ----- PeterN 29/12/2024----------------------------------
' This script returns an example sequenze of commands
' to a browser script "slideshow.html".
' slideshow.html displays local jpg-images and requests
' a controll command once per second.
' Commands are generated by this script at http://<IP-ADR>/msg?x=1
' This example send an endless sequenze.
' CMD$ may be set by a key button etc to remote controll the slideshow
cmd_go$ ="go"
cmd_play$ ="go"
cmd_stop$ ="stop"
cmd_next$ ="next"
cmd_previous$="previous"
cmd_repeat$ ="repeat"
onUrlMessage RETURN_CMD
while 1 'endlessly responds with command sequence
cmd$=cmd_go$ 'Restats slideshow with first pic
pause 5000
for i= 30 to 35
cmd$ = str$(i) 'displays foto030.jpg to foto35.jpg
pause 1200
next i
for i = 1 to 10
cmd$=CMD_next$ 'displays next pic
pause 2000
next i
pause 10000 '10s Pause
wlog "10s Pause"
for i = 1 to 3
cmd$=CMD_previous$ 'one pic back
pause 1000
next i
for i = 1 to 5
cmd$=CMD_next$ 'next pic
pause 3000
next i
WEND
END
WAIT
'---------------------------------------------------------
RETURN_CMD:
' Returns current CMD$ at http://<IP-ADR>/msg?x=1
' as next Command for requesting slideshow.html ;
' !BUT ONLY ONE TIME! Then will then return "NO_COMMAND"
UrlMsgReturn CMD$
wlog "Command returned is: " + CMD$
if CMD$ <> "NO_COMMAND" then CMD$="NO_COMMAND"
return
And here some explanations of the slideshow.html generated with PERPLEXITY.AI
Code: [Local Link Removed for Guests]
Summary of Features:
• Slideshow Functionality: Displays images in fullscreen mode with smooth transitions every 10 seconds.
• Manual Navigation:
• Users can navigate through images using:
• ArrowRight or `n` for the next image.
• ArrowLeft or `p` for the previous image, which will not go below index `0`.
• `r` to repeat the current image.
• `g` to restart from the first image.
• `s` to stop the slideshow.
• Browser Detection: Alerts users if they are not using Google Chrome or Microsoft Edge.
• Server Communication: Queries a server every second for commands or numeric inputs.
The HTML code utilizes AJAX to respond to specific commands for controlling a slideshow. Here’s a summary of how it works:
1. AJAX Requests:
• The script periodically queries a server using the `fetch` API to receive commands or numeric inputs that dictate which image to display next. This is done every second.
2. Command Handling:
• When the server responds, the code checks if the response is a number (indicating a specific image index) or a command (like “next” or “previous”).
• Based on the response, it updates the `currentIndex` variable to point to the appropriate image in the array.
3. Image Transition:
• The `changeImage` function is called with the new index, which creates an image element, sets its source, and applies a fade-in effect.
• If the command is “next”, it increments the index and wraps around if it exceeds the total number of images.
• If the command is “previous”, it decrements the index and ensures it does not go below zero.
4. User Interaction:
• The script also listens for keyboard events (like pressing arrow keys or specific letters) to manually control the slideshow, allowing users to navigate through images without waiting for automatic transitions.
This combination of AJAX for real-time command handling and JavaScript for dynamic content updates creates an interactive slideshow experience.