Building the Mystery Gang Image File Type Explorer

A Collaborative Development Story with Copilot

Documenting the process of building an educational, responsive image file type explorer for the web.

Project Kickoff: Defining the Educational App

I am a high school computer science teacher, teaching a Photoshop 2025 course next week. I want an app that will teach the students about the various file types of images found in web pages. In div#r1c1Div, I'd like some controls to cycle through some images that will be put in figures in div#r1c2Div. Below those controls, I'd like a summary of the nature of the file type of the image featured in the figure and its most common usage in a webpage. The images will be hosted in my images folder. What are the image types I should have? Also, I'll be featuring our dialog in a 'Chat Log' that will train students to effectively use Copilot in VSCode so please share lots of detail and be prepared to put our conversation as a running dialog in a separate webpage.

Here are the main image file types you should feature for web education:

  • JPEG (.jpg, .jpeg): Lossy compressed raster image. Best for photographs and complex images with gradients. Not good for transparency or sharp-edged graphics.
  • PNG (.png): Lossless compressed raster image, supports transparency. Great for graphics, logos, icons, and images needing transparency.
  • GIF (.gif): Lossless compressed raster image, supports animation, limited to 256 colors. Used for simple graphics, animations, and small icons.
  • SVG (.svg): Vector image format (XML-based). Scalable graphics, icons, and logos. Stays sharp at any size.
  • WebP (.webp): Modern image format by Google, supports both lossy and lossless compression, transparency, and animation. Efficient for web, smaller file sizes with good quality.

For your Chat Log, you can use a separate HTML page modeled after your existing documentation style.

Building the Responsive Gallery and Controls

I have put example image files in 'galleryImages' and am ready for you to create the content in the control panel/summary area and in the featured image area. I'd like the entire app to be responsive, so please tend to that. Choose a random image from the gallery, list its file name in a figcaption in the parent figure element and then describe the nature of the image based on its filetype in the controls area.

The app uses Bootstrap for responsiveness and JavaScript for interactivity. The controls allow cycling through images, and the summary area describes the file type. Here is the core logic:

app.js - Responsive Gallery Logic

const galleryImages = [
    { file: "galleryImages/noun-detective-6237323-1966AB.svg", type: "svg" },
    { file: "galleryImages/scooby.png", type: "png" },
    { file: "galleryImages/scoobyGang.gif", type: "gif" },
    { file: "galleryImages/scoobyGang.png", type: "png" },
    { file: "galleryImages/scoobyGangColorPage.jpg", type: "jpg" },
    { file: "galleryImages/scoobyGangColorPage.png", type: "png" },
    { file: "galleryImages/scoobyGangColorPageNoBg.png", type: "png" },
    { file: "galleryImages/scoobyWithRadialGradient.jpg", type: "jpg" },
    { file: "galleryImages/Mystery_Inc._members.webp", type: "webp" }
];

const typeDescriptions = {
    jpg: {
        name: "JPEG (.jpg, .jpeg)",
        desc: "Lossy compressed raster image. Best for photographs and complex images with gradients. Not good for transparency or sharp-edged graphics."
    },
    png: {
        name: "PNG (.png)",
        desc: "Lossless compressed raster image, supports transparency. Great for graphics, logos, icons, and images needing transparency."
    },
    gif: {
        name: "GIF (.gif)",
        desc: "Lossless compressed raster image, supports animation, limited to 256 colors. Used for simple graphics, animations, and small icons."
    },
    svg: {
        name: "SVG (.svg)",
        desc: "Vector image format (XML-based). Scalable graphics, icons, and logos. Stays sharp at any size."
    },
    webp: {
        name: "WebP (.webp)",
        desc: "Modern image format by Google, supports both lossy and lossless compression, transparency, and animation. Efficient for web, smaller file sizes with good quality."
    }
};

let currentIndex = Math.floor(Math.random() * galleryImages.length);

function updateDisplay() {
    const imgObj = galleryImages[currentIndex];
    const figure = document.getElementById("featuredFigure");
    figure.innerHTML = \`
        <img src="\${imgObj.file}" alt="Gallery Image" class="img-fluid rounded shadow mb-2" style="max-height:260px;">
        <figcaption class="small text-muted">\${imgObj.file.split('/').pop()}</figcaption>
    \`;

    const summaryDiv = document.getElementById("imgSummary");
    const info = typeDescriptions[imgObj.type];
    summaryDiv.innerHTML = \`
        <strong>\${info.name}</strong><br>
        \${info.desc}
    \`;
}

document.getElementById("prevBtn").addEventListener("click", function () {
    currentIndex = (currentIndex - 1 + galleryImages.length) % galleryImages.length;
    updateDisplay();
});

document.getElementById("nextBtn").addEventListener("click", function () {
    currentIndex = (currentIndex + 1) % galleryImages.length;
    updateDisplay();
});

// Initial display
updateDisplay();

This code ensures the app is responsive and accessible, and that students can cycle through and learn about each image type interactively.

Reflection and Next Steps

This is working very well! I'd like to start documenting our work. Using 'suggestedChatLog.html' as a model for how I want to do our documentation, can you take what we've done so far and summarize it in a file: mysteryGangChatLog.html?

This chat log demonstrates how GitHub Copilot can help scaffold, explain, and implement a responsive educational web app. The process included:

  • Identifying key image file types for web education
  • Designing a responsive UI using Bootstrap
  • Implementing interactive controls and dynamic summaries
  • Documenting the process for classroom use and future reference

The resulting app is ready for classroom demonstration and further extension!

Continuing the Adventure: Branding, Enhancements and Features

Can you suggest a short, snappy app name that captures the idea of file types and mystery?

Here are some ideas: Format Forensics, FileType Sleuth, The Extension Enigma, Format Detective, MimeType Mystery.
Format Forensics was chosen as the app name!

What Google Font would work well with "Format Forensics"?

Fira Mono is a great fit—modern, readable, and evokes a detective/tech vibe. Alternatives: Share Tech Mono, Special Elite, Roboto Mono.

What would a good tag line be for this app?

"Crack the Case of Web Image Formats!" was selected as the tag line.

Can you suggest a CSS background gradient for main that fits the idea of mystery and shadow, sampling colors from csLabTeens.jpg?

A dark, moody gradient was suggested and implemented:
background: linear-gradient(135deg, #363e69 0%, #6c48b3 40%, #5a5a98 80%, #dd850a 100%) !important;

Should we have included a bitmap in this discussion? Is it supported in a webpage?

BMP is technically supported in browsers, but is rarely used on the web due to large file sizes and lack of features. It's best to focus on JPEG, PNG, GIF, SVG, and WebP.

Let's make the intro more compact by moving it to a Bootstrap modal, launched by an orange button in the controls area.

The intro was moved to a modal, accessible via a prominent orange button in the controls area, improving mobile usability and keeping the interface clean.

Our file type descriptions include technical words. Can you make those words clickable in the summary, launching a modal with a student-friendly definition?

Implemented a glossary system: technical terms in the summary are highlighted and clickable, opening a Bootstrap modal with clear definitions and a mysterious color theme.

JavaScript: Glossary Modal System

// 1. Define your glossary terms and definitions (all keys lowercase)
const glossary = {
    "lossless": "A way of compressing data so that no information is lost. The original image can be perfectly reconstructed from the compressed data.",
    "compressed": "The file size is reduced to save space, often by removing unnecessary data or using algorithms to store information more efficiently.",
    "raster": "An image made up of a grid of pixels (tiny squares of color). Most photos and web images are raster images.",
    "gradients": "A gradual blend between two or more colors or shades, often used to create smooth transitions in images.",
    "lossy": "A type of compression that removes some data from the original file to make it smaller. This can reduce quality, but often not in a way that's easily noticed.",
    "compression": "The process of making a file smaller by encoding its data more efficiently.",
    "vector": "An image made from lines, shapes, and curves defined by math, not pixels. Vectors can be resized without losing quality.",
    "xml": "Extensible Markup Language. XML is a way to structure and store data using tags, similar to HTML. SVG images use XML to describe shapes, colors, and how things should look.",
    "scalable": "Can be resized (made bigger or smaller) without losing quality or becoming blurry."
};

// 2. Utility: Highlight glossary terms in a string of HTML
function highlightGlossaryTerms(text) {
    const glossaryTerms = Object.keys(glossary);
    // Regex matches any glossary term as a whole word, case-insensitive
    const regex = new RegExp(`\\b(${glossaryTerms.join("|")})\\b`, "gi");
    return text.replace(regex, (match) => {
        // Wrap the term in a span with a data attribute for lookup
        return `${match}`;
    });
}

// 3. In your updateDisplay function, use highlightGlossaryTerms for the summary
function updateDisplay() {
    // ...existing code to select the image and summary...
    summaryDiv.innerHTML = `
        ${info.name}
${highlightGlossaryTerms(info.desc)} `; // Add event listeners to each glossary term span summaryDiv.querySelectorAll('.glossary-term').forEach(span => { span.addEventListener('click', function() { const term = this.getAttribute('data-term'); document.getElementById('termModalLabel').textContent = term.charAt(0).toUpperCase() + term.slice(1); document.getElementById('termModalBody').textContent = glossary[term]; const modal = new bootstrap.Modal(document.getElementById('termModal')); modal.show(); }); }); }
  • Step 1: The glossary object holds all terms and their definitions, using lowercase keys for consistency.
  • Step 2: The highlightGlossaryTerms function finds glossary words in the summary and wraps them in a <span> with a special class and data-term attribute.
  • Step 3: In updateDisplay, after injecting the summary, event listeners are added to each glossary term. When clicked, the modal is populated with the term and its definition, then shown using Bootstrap's modal API.

This approach makes technical vocabulary interactive and accessible for students, while keeping your UI clean and engaging!

The glossary links are hard to read on the blue background. Can you make them stand out?

Added a text-shadow to the glossary terms for better readability: text-shadow: 1px 1px 3px #232946, 0 0 2px #000;

The detective emoji is a fun touch! How can we showcase it more?

The 🕵️‍♂️ emoji is used as a watermark in the featured image area, in the intro modal, and can be used as a mascot or badge throughout the app for a playful, thematic effect.

Can you make the detective emoji 50% bigger in the featured image area?

Increased the emoji's font-size from 2rem to 3rem for greater visual impact.

What a journey! Thanks for all the help, Copilot!

You're welcome! This collaborative adventure resulted in a polished, interactive, and educational app—ready for your students to crack the case of web image formats!

Bug Fixes: Cracking the Case of "XML"

When I clicked the glossary term "XML", the definition didn't appear in the modal. Is this a case-sensitivity problem?

Yes, the issue was due to case sensitivity. The glossary object used the key "XML" (uppercase), but the code looked up glossary["xml"] (lowercase), resulting in undefined.

Solution: Make all glossary keys lowercase and always use .toLowerCase() for lookups.

JavaScript: Case-Insensitive Glossary Fix

// Glossary object: all keys are lowercase
const glossary = {
    "lossless": "...",
    // ... other terms ...
    "xml": "Extensible Markup Language. XML is a way to structure and store data using tags, similar to HTML. SVG images use XML to describe shapes, colors, and how things should look.",
    "scalable": "..."
};

// When highlighting and looking up terms, always use .toLowerCase()
function highlightGlossaryTerms(text) {
    const glossaryTerms = Object.keys(glossary);
    const regex = new RegExp(`\\b(${glossaryTerms.join("|")})\\b`, "gi");
    return text.replace(regex, (match) => {
        return `${match}`;
    });
}

// In the event listener:
span.addEventListener('click', function() {
    const term = this.getAttribute('data-term');
    document.getElementById('termModalLabel').textContent = term.charAt(0).toUpperCase() + term.slice(1);
    document.getElementById('termModalBody').textContent = glossary[term]; // always lowercase
    const modal = new bootstrap.Modal(document.getElementById('termModal'));
    modal.show();
});

Result: Clicking "XML" (or any glossary term, regardless of case) now correctly displays the definition in the modal.

Image Selection: Optional Randomness

I'd like users to be able to choose whether images load randomly or sequentially. Can we add a toggle for this in the controls, and default to sequential order?

Added a Bootstrap switch labeled Random Order above the navigation buttons. When enabled, images are chosen randomly; when off (default), images cycle sequentially.

HTML: Add the Random Order Toggle

<div class="form-check form-switch mb-2">
  <input class="form-check-input" type="checkbox" id="randomModeSwitch">
  <label class="form-check-label" for="randomModeSwitch" style="user-select:none;">Random Order</label>
</div>

JavaScript: Random/Sequential Logic

let currentIndex = 0; // Always start with the first image
let randomMode = false; // Default to sequential

const randomModeSwitch = document.getElementById("randomModeSwitch");
if (randomModeSwitch) {
    randomModeSwitch.checked = false;
    randomModeSwitch.addEventListener("change", function () {
        randomMode = this.checked;
        // If switching to random, pick a random image immediately
        if (randomMode) {
            currentIndex = Math.floor(Math.random() * galleryImages.length);
            updateDisplay();
        }
    });
}

document.getElementById("prevBtn").addEventListener("click", function () {
    if (randomMode) {
        let nextIndex;
        do {
            nextIndex = Math.floor(Math.random() * galleryImages.length);
        } while (galleryImages.length > 1 && nextIndex === currentIndex);
        currentIndex = nextIndex;
    } else {
        currentIndex = (currentIndex - 1 + galleryImages.length) % galleryImages.length;
    }
    updateDisplay();
});

document.getElementById("nextBtn").addEventListener("click", function () {
    if (randomMode) {
        let nextIndex;
        do {
            nextIndex = Math.floor(Math.random() * galleryImages.length);
        } while (galleryImages.length > 1 && nextIndex === currentIndex);
        currentIndex = nextIndex;
    } else {
        currentIndex = (currentIndex + 1) % galleryImages.length;
    }
    updateDisplay();
});
  • Why this is needed: It gives users control over how they explore the gallery—either in order or by surprise.
  • What went wrong at first: The initial code set currentIndex to a random value on every page load, even when random mode was off. This caused a different image to appear each time, even in sequential mode.
  • How it was fixed: currentIndex is now set to 0 by default, so the first image always loads in sequential mode. A random image is only chosen when the user enables random mode.

This feature makes the app more interactive and lets students explore image formats in the way that suits them best!

User-Sensitive Navigation: Quick Top & Bottom Buttons

Our chat log is getting long! Can we add subtle "Top" and "Bottom" buttons so users can quickly jump to the top or bottom of the page?

Added floating "Top" and "Bottom" buttons to the lower right corner of the page. These use smooth scrolling for a user-friendly experience and help users navigate long documentation efficiently.

HTML & JavaScript: Floating Navigation Buttons

<div id="navButtons" style="position:fixed; right:1rem; bottom:2.5rem; z-index:9999; display:flex; flex-direction:column; gap:0.5rem;">
    <button id="toTopBtn" class="btn btn-outline-secondary btn-sm" title="Go to Top" style="opacity:0.7;">Top</button>
    <button id="toBottomBtn" class="btn btn-outline-secondary btn-sm" title="Go to Bottom" style="opacity:0.7;">Bottom</button>
</div>

<script>
document.getElementById('toTopBtn').onclick = function() {
    window.scrollTo({ top: 0, behavior: 'smooth' });
};
document.getElementById('toBottomBtn').onclick = function() {
    window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
};
</script>

This makes navigating your growing chat log much easier for students and teachers alike!

Reflections: What If We Used Gemini or Claude?

What might have been different in our development had we used a different Copilot model such as Gemini or Claude?

If we had used a different AI model like Gemini (by Google) or Claude (by Anthropic), the development experience might have varied in several ways:

  • Style & Tone: Each model has its own conversational style and way of explaining concepts. Gemini might offer more integration with Google’s ecosystem, while Claude is known for its focus on safety and clarity.
  • Code Suggestions: The quality and specificity of code snippets, explanations, and best practices might differ. Some models may be more concise, while others provide more context or alternative approaches.
  • Creativity & Problem Solving: Different models may suggest unique solutions or creative ideas for UI/UX, naming, or educational strategies.
  • Integration: GitHub Copilot is tightly integrated with VS Code and GitHub workflows. Gemini or Claude might require different tools or plugins for seamless coding assistance.
  • Safety & Ethics: Claude, for example, is designed with strong safety guardrails, which might affect how it responds to certain requests or clarifies ethical considerations.

Summary: While the core development process would be similar, the nuances of guidance, code style, and user experience would reflect the strengths and design philosophies of each AI model. Exploring multiple models can help teams find the best fit for their workflow and learning goals!