evaluate this app, and how to make it to load more faster?   <script>
    class ImageCache {
      constructor() {
        this.cache = new Map();
      }

      async getImage(url) {
        if (this.cache.has(url)) {
          return this.cache.get(url);
        }

        try {
          const imageData = await this.loadImage(url);
          this.cache.set(url, imageData);
          return imageData;
        } catch (error) {
          throw new Error(`Failed to load image: ${error.message}`);
        }
      }

      async loadImage(url) {
        return new Promise((resolve, reject) => {
          const img = new Image();
          img.crossOrigin = 'anonymous';
          img.onload = () => {
            const canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            resolve({
              dataUrl: canvas.toDataURL('image/jpeg'),
              width: img.width,
              height: img.height,
              element: img
            });
          };
          img.onerror = () => reject(new Error('Image load failed'));
          img.src = url;
        });
      }

      clear() {
        this.cache.clear();
      }
    }

    class PollinationsAPI {
      static retryDelay = 1000; // ms
      static maxRetries = 5;
      static requestQueue = [];
      static isProcessingQueue = false;
      static rateLimitDelay = 1000; // 1 second

      static async fetchWithRetry(url, options = {}, retries = this.maxRetries) {
        try {
          const response = await fetch(url, options);
          if (response.status === 429) {
            throw new Error('RATE_LIMIT');
          }
          if (!response.ok) {
            throw new Error('FETCH_ERROR');
          }
          return response;
        } catch (error) {
          if (retries === 0) throw error;
          if (error.message === 'RATE_LIMIT') {
            await new Promise(res => setTimeout(res, this.rateLimitDelay));
          } else {
            await new Promise(res => setTimeout(res, this.retryDelay * (this.maxRetries - retries + 1)));
          }
          return this.fetchWithRetry(url, options, retries - 1);
        }
      }

      static async queueRequest(requestFn) {
        return new Promise((resolve, reject) => {
          this.requestQueue.push({ requestFn, resolve, reject });
          if (!this.isProcessingQueue) this.processQueue();
        });
      }

      static async processQueue() {
        if (this.requestQueue.length === 0) {
          this.isProcessingQueue = false;
          return;
        }
        this.isProcessingQueue = true;
        const { requestFn, resolve, reject } = this.requestQueue.shift();
        try {
          const result = await requestFn();
          resolve(result);
        } catch (error) {
          reject(error);
        }
        await new Promise(res => setTimeout(res, this.rateLimitDelay));
        this.processQueue();
      }

      static async generateText(prompt) {
        return this.queueRequest(async () => {
          try {
            const url = `https://text.pollinations.ai/${encodeURIComponent(prompt)}`;
            const response = await this.fetchWithRetry(url);
            return response.text();
          } catch (error) {
            if (error.message === 'RATE_LIMIT') {
              throw new Error('Rate limit exceeded. Please try again later.');
            }
            throw new Error('Failed to generate text.');
          }
        });
      }

      static async generateImage(prompt, settings = {}) {
        return this.queueRequest(async () => {
          const baseUrl = 'https://image.pollinations.ai/prompt/';
          const params = new URLSearchParams({
            width: settings.width || 1024,
            height: settings.height || 576,
            seed: settings.seed || Math.floor(Math.random() * 1000000),
            nologo: 'true',
            model: settings.model || 'flux',
            private: 'true',
            t: Date.now()
          });

          const encodedPrompt = encodeURIComponent(prompt);
          const url = `${baseUrl}${encodedPrompt}?${params}`;

          try {
            const response = await this.fetchWithRetry(url);
            return response.url;
          } catch (error) {
            if (error.message === 'RATE_LIMIT') {
              throw new Error('Rate limit exceeded. Please try again later.');
            }
            throw new Error('Failed to generate image.');
          }
        });
      }
    }

    class LyricalStorybookCreator {
      constructor() {
        this.currentPage = 0;
        this.pages = [];
        this.isGenerating = false;
        this.selectedStyle = null;
        this.darkMode = true;

        // New property for song title input
        this.songTitleInput = document.getElementById('songTitle');

        // New property for image model selection
        this.imageModelSelect = document.getElementById('imageModel');

        // New property for image caching
        this.imageCache = new ImageCache();

        // UI Elements
        this.lyricsInput = document.getElementById('lyrics');
        this.themeInput = document.getElementById('theme');
        this.artStyleSelect = document.getElementById('artStyle');
        this.colorPaletteSelect = document.getElementById('colorPalette');
        this.pageLayoutSelect = document.querySelector('.layout-controls .layout-select');
        this.textStyleSelect = document.getElementById('textStyle');
        this.fontFamilySelect = document.getElementById('fontFamily');
        this.exportBtn = document.querySelector('.layout-controls #exportBtn');
        this.generateBtn = document.getElementById('generateBtn');
        this.resetBtn = document.getElementById('resetBtn');
        this.previewArea = document.getElementById('previewArea');
        this.messageBox = document.getElementById('messageBox');
        this.paginationControls = document.getElementById('paginationControls');
        this.prevPageBtn = document.getElementById('prevPageBtn');
        this.nextPageBtn = document.getElementById('nextPageBtn');
        this.pageIndicator = document.getElementById('pageIndicator');
        this.modeToggle = document.getElementById('modeToggle');

        // Add new property to track layout controls container
        this.layoutControls = document.querySelector('.layout-controls');

        // Initially hide layout controls
        this.layoutControls.style.display = 'none';

        this.bindEvents();
      }

      bindEvents() {
        this.generateBtn.addEventListener('click', () => this.generateStorybook());
        this.exportBtn.addEventListener('click', () => this.exportPDF());
        this.resetBtn.addEventListener('click', () => this.resetApp());
        this.prevPageBtn.addEventListener('click', () => this.navigatePage(-1));
        this.nextPageBtn.addEventListener('click', () => this.navigatePage(1));
        this.pageLayoutSelect.addEventListener('change', () => {
          if (this.pages.length > 0) {
            this.updatePreview();
          }
        });
        this.textStyleSelect.addEventListener('change', () => {
          if (this.pages.length > 0) {
            this.updatePreview();
          }
        });
        this.fontFamilySelect.addEventListener('change', () => {
          if (this.pages.length > 0) {
            this.updatePreview();
          }
        });
        this.modeToggle.addEventListener('change', () => this.toggleTheme());

        // Swipe Navigation for Mobile
        this.previewArea.addEventListener('touchstart', this.handleTouchStart.bind(this), false);
        this.previewArea.addEventListener('touchend', this.handleTouchEnd.bind(this), false);
        this.xDown = null;
      }

      handleTouchStart(evt) {
        const firstTouch = evt.touches[0];
        this.xDown = firstTouch.clientX;
      }

      handleTouchEnd(evt) {
        if (!this.xDown) return;
        const xUp = evt.changedTouches[0].clientX;
        const deltaX = this.xDown - xUp;
        const threshold = 50; // Required min distance for swipe

        if (deltaX > threshold) {
          this.navigatePage(1);
        } else if (deltaX < -threshold) {
          this.navigatePage(-1);
        }
        this.xDown = null;
      }

      showMessage(message, type = 'success') {
        this.messageBox.textContent = message;
        this.messageBox.className = `message ${type}`;
        this.messageBox.style.display = 'block';
        setTimeout(() => {
          this.messageBox.style.display = 'none';
        }, 4000);
      }

      async generateStorybook() {
        if (this.isGenerating) return;

        // Hide layout controls during generation
        this.layoutControls.style.display = 'none';

        // Set default page layout to single
        this.selectedStyle = {
          artStyle: this.artStyleSelect.value,
          colorPalette: this.colorPaletteSelect.value,
          pageLayout: 'single'
        };

        const lyrics = this.lyricsInput.value.trim();
        const theme = this.themeInput.value.trim();
        const title = this.songTitleInput.value.trim(); // Capture title

        if (!lyrics || !theme || !title) {
          this.showMessage('Please enter song title, lyrics, and theme.', 'error');
          return;
        }

        this.isGenerating = true;
        this.generateBtn.classList.add('generating');
        this.generateBtn.disabled = true;

        this.previewArea.innerHTML = `
          <div class="loading-container">
            <div class="progress-container">
              <div class="progress-bar" id="generationProgress"></div>
            </div>
            <div class="progress-text" id="progressText">
              Preparing to generate your storybook...
            </div>
          </div>
        `;

        const progressBar = document.querySelector('#generationProgress');
        const progressText = document.querySelector('#progressText');
        const rawSegments = lyrics.split('\n').filter(line => line.trim() !== '');
        const maxPages = rawSegments.length <= 12 ? 3 : 4; // Use 3 pages for shorter lyrics, 4 for longer
        const segmentsPerPage = Math.ceil(rawSegments.length / maxPages);
        const segments = [];

        for (let i = 0; i < rawSegments.length; i += segmentsPerPage) {
          segments.push(rawSegments.slice(i, i + segmentsPerPage).join(' '));
        }

        this.pages = [];
        const totalSteps = segments.length;
        let currentStep = 0;

        try {
          for (let i = 0; i < segments.length && i < maxPages; i++) {
            currentStep++;
            const segment = segments[i];
            const progress = Math.round((currentStep / totalSteps) * 100);
            progressBar.style.width = `${progress}%`;
            progressText.textContent = `Creating page ${currentStep} of ${totalSteps} (${progress}% complete)`;

            // Generate scene description
            const sceneDescription = await PollinationsAPI.generateText(`
              Create a vivid, detailed description for a storybook illustration. 
              Context: These song lyrics: "${segment}"
              Theme: ${theme}
              Style: ${this.selectedStyle.artStyle}
              Consider:
              - The emotional tone and mood
              - Key symbols and metaphors
              - Color palette and lighting
              - Character expressions and poses
              - Environmental details and setting
              - Composition and focal points
              Describe the scene in rich, artistic detail.
            `);

            // Generate image prompt
            const imagePrompt = await this.buildImagePrompt(this.selectedStyle.artStyle, this.selectedStyle.colorPalette, theme, sceneDescription);
            const imageData = await this.imageCache.getImage(await PollinationsAPI.generateImage(imagePrompt, { width: 1280, height: 720, model: this.imageModelSelect.value }));

            this.pages.push({
              lyrics: segment,
              description: sceneDescription,
              imageUrl: imageData.dataUrl
            });

            this.showMessage(`Page ${i + 1} of ${segments.length} generated.`, 'success');
          }

          this.currentPage = 0;

          // After successful generation, show the layout controls
          this.layoutControls.style.display = 'flex';
          this.updatePreview();
        } catch (error) {
          console.error(error);
          this.showMessage(error.message, 'error');
          this.previewArea.innerHTML = `<p>Error generating storybook: ${error.message}</p>`;
          // Keep layout controls hidden on error
          this.layoutControls.style.display = 'none';
        } finally {
          this.isGenerating = false;
          this.generateBtn.classList.remove('generating');
          this.generateBtn.disabled = false;
        }
      }

      async buildImagePrompt(artStyle, colorPalette, theme, sceneDescription) {
        // Base quality parameters
        const qualityParams = "masterpiece quality, highly detailed, sharp focus, professional lighting";

        // Color palette prompts
        const colorPalettes = {
          'vibrant': "vibrant saturated colors, bold color combinations",
          'pastel': "soft pastel colors, gentle muted tones",
          'monochrome': "monochromatic color scheme, variations in tone",
          'earthy': "natural earth tones, organic color palette",
          'neon': "bright neon colors, electric hues",
          'vintage': "faded vintage colors, retro color palette",
          'cool': "cool color temperature, blues, purples",
          'warm': "warm color temperature, reds, oranges",
          'jewel': "rich jewel tones, deep saturated colors",
          'grayscale': "black and white, grayscale values"
        };

        const selectedPalette = colorPalettes[colorPalette] || colorPalettes['vibrant'];

        // Style-specific prompts
        const stylePrompts = {
          'whimsical': "whimsical and playful illustration, magical atmosphere, enchanted storybook style",
          'photorealistic': "hyperrealistic detailed illustration, cinematic lighting, dramatic atmosphere",
          'sketch': "detailed hand-drawn sketch, artistic linework, expressive strokes",
          'pencil': "detailed pencil drawing, fine graphite shading, delicate linework",
          'vintage': "classic vintage storybook art, traditional illustration style",
          'cartoon': "animated storybook style, expressive characters, bold colors",
          'exaggerated': "stylized artistic illustration, bold and dynamic composition",
          'watercolor': "delicate watercolor illustration, soft color transitions",
          'acrylic': "rich acrylic painting style, bold brushstrokes",
          'digital': "modern digital art, clean refined illustration",
          'pop': "pop art illustration style, bold colors, graphic elements",
          'woodcut': "traditional woodcut print style, bold contrasting lines"
        };

        // Composition elements
        const composition = [
          "balanced composition",
          "attention to detail",
          selectedPalette,
          "dynamic lighting",
          "emotional storytelling through visuals"
        ].join(", ");

        // Build final prompt
        return `${stylePrompts[artStyle]}, 
          ${sceneDescription},
          ${theme},
          ${selectedPalette},
          storybook illustration style,
          ${composition},
          ${qualityParams},
          suitable for narrative illustration,
          story-driven artwork,
          emotional depth,
          artistic excellence,
          trending on artstation`;
      }

      updatePreview() {
        if (this.pages.length === 0) {
          this.previewArea.innerHTML = '<p>No pages to display. Generate your storybook first.</p>';
          this.paginationControls.style.display = 'none';
          return;
        }

        const page = this.pages[this.currentPage];
        const layoutClass = (() => {
          switch(this.pageLayoutSelect.value) {
            case 'spread': return 'spread-layout';
            case 'comic': return 'comic-layout';
            default: return '';
          }
        })();

        const fontFamily = this.fontFamilySelect.value;
        const textStyleClass = this.textStyleSelect.value !== 'default' ? this.textStyleSelect.value : '';

        this.previewArea.innerHTML = `
          <div class="storybook-page ${layoutClass}">
            <img src="${page.imageUrl}" alt="Story Illustration">
            <div class="page-text ${textStyleClass}" style="font-family: ${fontFamily};">
              <p>${page.lyrics}</p>
            </div>
          </div>
        `;

        // Update pagination controls
        this.pageIndicator.textContent = `Page ${this.currentPage + 1} of ${this.pages.length}`;
        this.paginationControls.style.display = 'flex';
        this.prevPageBtn.disabled = this.currentPage === 0;
        this.nextPageBtn.disabled = this.currentPage === this.pages.length - 1;
      }

      navigatePage(direction) {
        const newPage = this.currentPage + direction;
        if (newPage >= 0 && newPage < this.pages.length) {
          this.currentPage = newPage;
          this.updatePreview();
        }
      }

      async exportPDF() {
        if (this.pages.length === 0) {
          this.showMessage('No storybook to export. Please generate it first.', 'error');
          return;
        }

        this.exportBtn.classList.add('generating');
        this.exportBtn.disabled = true;
        this.showMessage('Exporting your storybook as PDF...', 'success');

        try {
          const songTitle = this.songTitleInput.value; // Capture the title outside the header function

          const docDefinition = {
            content: [],
            pageSize: 'A4',
            pageMargins: [40, 60, 40, 60],
            pageOrientation: this.pageLayoutSelect.value === 'comic' ? 'landscape' : 'portrait',
            defaultStyle: {
              font: 'Roboto',
              fontSize: 12,
              lineHeight: 1.6,
              color: document.body.classList.contains('dark-mode') ? '#fff9eb' : '#2c3e50' // Match site text color
            },
            fonts: {
              Roboto: {
                normal: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Regular.ttf',
                bold: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Medium.ttf',
                italics: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Italic.ttf',
                bolditalics: 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-MediumItalic.ttf'
              }
            },
            // Add header with song title only on the first page
            header: function(currentPage) {
              if (currentPage === 1) {
                return {
                  text: `${songTitle || 'Lyrical Storybook'}`,
                  alignment: 'center',
                  fontSize: 18,
                  bold: true,
                  margin: [0, 20, 0, 20],
                  color: document.body.classList.contains('dark-mode') ? '#fff9eb' : '#2c3e50'
                };
              }
              return null;
            },
            footer: function(currentPage, pageCount) {
              return {
                text: `Page ${currentPage} of ${pageCount}`,
                alignment: 'right',
                margin: [0, 0, 40, 0],
                fontSize: 10,
                italics: true,
                color: document.body.classList.contains('dark-mode') ? '#e2d9f3' : '#34495e' // Match site secondary text color
              };
            },
            background: function(currentPage, pageSize) {
              return {
                canvas: [
                  {
                    type: 'rect',
                    x: 0,
                    y: 0,
                    w: pageSize.width,
                    h: pageSize.height,
                    color: document.body.classList.contains('dark-mode') ? '#2a1b3d' : '#f8f4e8'
                  }
                ]
              };
            }
          };

          const textStyle = this.textStyleSelect.value;
          const getTextStyling = () => {
            const textColor = document.body.classList.contains('dark-mode') ? '#fff9eb' : '#2c3e50';
            const baseStyle = {
              font: 'Roboto',
              fontSize: 12,
              lineHeight: 1.6,
              color: textColor
            };
            
            const styles = {
              'elegant': { ...baseStyle, italics: true, fontSize: 13 },
              'playful': { ...baseStyle, fontSize: 13, bold: true },
              'fairytale': { ...baseStyle, fontSize: 13, italics: true },
              'minimal': { ...baseStyle, fontSize: 12 }
            };
            
            return styles[textStyle] || baseStyle;
          };

          const addPageBreak = () => {
            docDefinition.content.push({ text: '', pageBreak: 'after' });
          };

          for (let i = 0; i < this.pages.length; i++) {
            const page = this.pages[i];
            const imgData = await this.imageCache.getImage(page.imageUrl);
            const textStyling = getTextStyling();
            
            switch(this.pageLayoutSelect.value) {
              case 'spread':
                docDefinition.content.push({
                  columns: [
                    {
                      width: '45%',
                      stack: [{
                        image: imgData.dataUrl,
                        fit: [350, 500],
                        alignment: 'center',
                        margin: [0, 0, 20, 0]
                      }]
                    },
                    {
                      width: '45%',
                      stack: [{
                        text: page.lyrics,
                        ...textStyling,
                        alignment: 'left',
                        margin: [20, 0, 0, 0]
                      }]
                    }
                  ],
                  columnGap: 40,
                  margin: [0, 20, 0, 20]
                });
                break;

              case 'comic':
                docDefinition.content.push({
                  stack: [
                    {
                      image: imgData.dataUrl,
                      fit: [500, 300],
                      alignment: 'center',
                      margin: [0, 0, 0, 10]
                    },
                    {
                      text: page.lyrics,
                      ...textStyling,
                      alignment: 'center',
                      margin: [20, 0, 20, 20]
                    }
                  ],
                  margin: [0, 20, 0, 20]
                });
                break;

              default:
                docDefinition.content.push({
                  stack: [
                    {
                      image: imgData.dataUrl,
                      width: 400,
                      alignment: 'center',
                      margin: [0, 0, 0, 20]
                    },
                    {
                      text: page.lyrics,
                      ...textStyling,
                      alignment: 'center',
                      margin: [40, 0, 40, 20]
                    }
                  ],
                  margin: [0, 20, 0, 20]
                });
            }

            if (i < this.pages.length - 1) {
              addPageBreak();
            }
          }

          pdfMake.createPdf(docDefinition).download('Lyrical_Storybook.pdf');
          this.showMessage('Storybook exported successfully!', 'success');

        } catch (error) {
          console.error(error);
          this.showMessage('Failed to export PDF. Please try again.', 'error');
        } finally {
          this.exportBtn.classList.remove('generating');
          this.exportBtn.disabled = false;
        }
      }

      async getImageData(url) {
        try {
          return await this.imageCache.getImage(url);
        } catch (error) {
          console.error('Image load error:', error);
          throw error;
        }
      }

      resetApp() {
        // Clear Inputs
        this.lyricsInput.value = '';
        this.themeInput.value = '';
        this.songTitleInput.value = '';
        this.artStyleSelect.value = 'woodcut';
        this.colorPaletteSelect.value = 'vibrant';
        this.imageModelSelect.value = 'flux';
        this.pageLayoutSelect.value = 'single';
        this.textStyleSelect.value = 'default';
        this.fontFamilySelect.value = 'Roboto';

        // Reset pages and current page
        this.pages = [];
        this.currentPage = 0;

        // Clear preview area and hide controls
        this.previewArea.innerHTML = '<p>Preview will appear here once you generate your storybook.</p>';
        this.paginationControls.style.display = 'none';
        this.layoutControls.style.display = 'none';

        // Clear any messages
        this.messageBox.style.display = 'none';

        // Clear image cache
        this.imageCache.clear();

        // Reset generating state
        this.isGenerating = false;
        this.generateBtn.classList.remove('generating');
        this.generateBtn.disabled = false;

        // Show confirmation message
        this.showMessage('All fields have been reset', 'success');
      }

      toggleTheme() {
        this.darkMode = !this.darkMode;
        document.body.classList.toggle('light-mode', !this.darkMode);
        document.body.classList.toggle('dark-mode', this.darkMode);
      }
    }

    // Initialize the Application on DOM Content Loaded
    document.addEventListener('DOMContentLoaded', () => {
      window.storybookCreator = new LyricalStorybookCreator();
    });
  </script>