diff --git a/src/components/AudioArea.vue b/src/components/AudioArea.vue
index 2be1cad..1b3078f 100644
--- a/src/components/AudioArea.vue
+++ b/src/components/AudioArea.vue
@@ -5,6 +5,7 @@
-
+
diff --git a/src/services/AudioLoader.ts b/src/services/AudioLoader.ts
new file mode 100644
index 0000000..0e75fc8
--- /dev/null
+++ b/src/services/AudioLoader.ts
@@ -0,0 +1,170 @@
+/**
+ * Global audio loading queue service to prevent hitting browser connection limits
+ */
+
+// Configuration
+const MAX_CONCURRENT_LOADS = 3;
+
+// State
+let activeLoads = 0;
+const audioQueue: Array<{
+ src: string;
+ onComplete: (blobUrl: string) => void;
+ onError: (error: Error) => void;
+}> = [];
+
+// Cache of loaded audio files (src -> blobUrl)
+const loadedAudioCache: Record = {};
+
+// Keep track of pending loads to avoid duplicates
+const pendingLoads: Set = new Set();
+
+/**
+ * Queue an audio file for loading, respecting the global concurrent loading limit
+ * Returns a promise that resolves with the blob URL when loading is complete
+ */
+export function queueAudioForLoading(
+ src: string,
+ onComplete?: (blobUrl: string) => void,
+ onError?: (error: Error) => void
+): Promise {
+ // Return cached result immediately if available
+ if (loadedAudioCache[src]) {
+ console.debug(`[AudioLoader] Using cached audio for ${src}`);
+ const blobUrl = loadedAudioCache[src];
+ if (onComplete) setTimeout(() => onComplete(blobUrl), 0);
+ return Promise.resolve(blobUrl);
+ }
+
+ // If this source is already being loaded, add to existing promises
+ if (pendingLoads.has(src)) {
+ console.debug(`[AudioLoader] Already loading ${src}, adding to pending requests`);
+ return new Promise((resolve, reject) => {
+ audioQueue.push({
+ src,
+ onComplete: (blobUrl) => {
+ if (onComplete) onComplete(blobUrl);
+ resolve(blobUrl);
+ },
+ onError: (error) => {
+ if (onError) onError(error);
+ reject(error);
+ }
+ });
+ });
+ }
+
+ // Mark as pending
+ pendingLoads.add(src);
+
+ return new Promise((resolve, reject) => {
+ // Add to queue
+ audioQueue.push({
+ src,
+ onComplete: (blobUrl) => {
+ if (onComplete) onComplete(blobUrl);
+ resolve(blobUrl);
+ },
+ onError: (error) => {
+ if (onError) onError(error);
+ reject(error);
+ }
+ });
+
+ // Try to process queue
+ processQueue();
+ });
+}
+
+/**
+ * Process the next items in the queue if we have capacity
+ */
+function processQueue() {
+ // Group queue items by src to avoid duplicate loads
+ const nextBatch: Record void;
+ onError: (error: Error) => void;
+ }>> = {};
+
+ // Find next items to process while respecting MAX_CONCURRENT_LOADS
+ while (activeLoads < MAX_CONCURRENT_LOADS && audioQueue.length > 0) {
+ const next = audioQueue.shift();
+ if (!next) continue;
+
+ // If already cached, complete immediately without consuming a slot
+ if (loadedAudioCache[next.src]) {
+ next.onComplete(loadedAudioCache[next.src]);
+ continue;
+ }
+
+ // Group by src
+ if (!nextBatch[next.src]) {
+ nextBatch[next.src] = [];
+ // Each unique src counts as one active load
+ activeLoads++;
+ }
+
+ nextBatch[next.src].push({
+ onComplete: next.onComplete,
+ onError: next.onError
+ });
+ }
+
+ // Start loading each unique audio file
+ Object.entries(nextBatch).forEach(([src, handlers]) => {
+ loadAudio(src, handlers);
+ });
+}
+
+/**
+ * Internal function to handle the actual audio loading using fetch API
+ * This ensures the entire audio file is downloaded
+ * @param src The source URL to load
+ * @param handlers Array of handlers to call when loading completes or fails
+ */
+async function loadAudio(
+ src: string,
+ handlers: Array<{
+ onComplete: (blobUrl: string) => void;
+ onError: (error: Error) => void;
+ }>
+) {
+ console.debug(`[AudioLoader] Loading ${src} (${handlers.length} listeners)`);
+
+ try {
+ // Fetch the entire audio file
+ const response = await fetch(src);
+
+ if (!response.ok) {
+ throw new Error(`Failed to load audio: ${response.statusText}`);
+ }
+
+ // Convert to blob to ensure full download
+ const blob = await response.blob();
+
+ // Create a blob URL to use as the audio source
+ const blobUrl = URL.createObjectURL(blob);
+
+ // Store in cache
+ loadedAudioCache[src] = blobUrl;
+
+ console.debug(`[AudioLoader] Successfully loaded ${src}`);
+
+ // Call all completion handlers
+ handlers.forEach(handler => handler.onComplete(blobUrl));
+ } catch (error) {
+ console.error(`[AudioLoader] Error loading audio: ${error}`);
+
+ // Call all error handlers
+ handlers.forEach(handler => handler.onError(error as Error));
+ } finally {
+ // Remove from pending loads
+ pendingLoads.delete(src);
+
+ // Decrement counter when audio is loaded (or failed)
+ activeLoads--;
+
+ // Process next audio in queue if available
+ processQueue();
+ }
+}