PageSourceSearch

https://asigalov61.github.io/

html asigalov61.github.io collected 2026-10-03 08:39:54 UTC 25,819 bytes, 599 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="UTF-8" />
5  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6  <title>MIDI Melody Generator</title>
7  <!-- Reminder: cdn.tailwindcss.com should not be used in production -->
8  
8<script src="https://cdn.tailwindcss.com"></script>
8
9  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10  <style>
11    /* Import fonts */
12    @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
13
14    body {
15      font-family: 'Poppins', sans-serif;
16      background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
17      min-height: 100vh;
18    }
19
20    .gradient-bg {
21      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
22    }
23
24    .file-upload {
25      border: 2px dashed #cbd5e0;
26      transition: all 0.3s ease;
27    }
28
29    .file-upload:hover {
30      border-color: #667eea;
31      transform: translateY(-2px);
32    }
33
34    .file-upload.dragover {
35      border-color: #667eea;
36      background-color: rgba(102, 126, 234, 0.1);
37    }
38
39    .parameter-slider::-webkit-slider-thumb {
40      -webkit-appearance: none;
41      appearance: none;
42      width: 20px;
43      height: 20px;
44      border-radius: 50%;
45      background: #667eea;
46      cursor: pointer;
47    }
48
49    .result-card {
50      box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
51                  0 4px 6px -2px rgba(0, 0, 0, 0.05);
52      transition: all 0.3s ease;
53    }
54
55    .result-card:hover {
56      transform: translateY(-5px);
57      box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1),
58                  0 10px 10px -5px rgba(0, 0, 0, 0.04);
59    }
60
61    .loading-spinner {
62      animation: spin 1s linear infinite;
63    }
64
65    @keyframes spin {
66      0% { transform: rotate(0deg); }
67      100% { transform: rotate(360deg); }
68    }
69
70    /* Audio visualizer canvas styling */
71    #audioVisualizer {
72      background-color: #f5f7fa;
73      border-radius: 0.5rem;
74      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
75    }
76  </style>
77</head>
78<body class="text-gray-800">
79  <div class="container mx-auto px-4 py-8 max-w-6xl">
80    <!-- Header -->
81    <header class="gradient-bg text-white rounded-xl p-6 mb-8 shadow-lg">
82      <div class="flex flex-col md:flex-row items-center justify-between">
83        <div class="mb-4 md:mb-0">
84          <h1 class="text-3xl font-bold">
85            <i class="fas fa-music mr-2"></i> MIDI Melody Generator
86          </h1>
87          <p class="mt-2 opacity-90">Transform your MIDI files with expressive melodies</p>
88        </div>
89        <div class="flex items-center space-x-2">
90          <span class="px-3 py-1 bg-white bg-opacity-20 rounded-full text-sm">Hugging Face</span>
91          <span class="px-3 py-1 bg-white bg-opacity-20 rounded-full text-sm">AI Music</span>
92        </div>
93      </div>
94    </header>
95
96    <!-- Main Content -->
97    <main class="space-y-8">
98      <!-- Input Section -->
99      <section class="bg-white rounded-xl p-6 shadow-md">
100        <h2 class="text-xl font-semibold mb-4 flex items-center">
101          <i class="fas fa-upload mr-2 text-indigo-500"></i> Upload MIDI File
102        </h2>
103        
104        <div id="fileDropArea" class="file-upload rounded-lg p-8 text-center cursor-pointer mb-6">
105          <div class="flex flex-col items-center justify-center">
106            <i class="fas fa-cloud-upload-alt text-4xl text-indigo-400 mb-3"></i>
107            <p class="font-medium">Drag &amp; drop your MIDI file here</p>
108            <p class="text-gray-500 text-sm mt-1">or click to browse files</p>
109            <input type="file" id="fileInput" class="hidden" accept=".mid,.midi">
110          </div>
111        </div>
112        
113        <div id="fileNameDisplay" class="hidden bg-gray-50 rounded-lg p-4 mb-6">
114          <div class="flex items-center justify-between">
115            <div class="flex items-center">
116              <i class="fas fa-file-audio text-indigo-500 mr-3 text-xl"></i>
117              <div>
118                <p class="font-medium" id="fileName">sample.mid</p>
119                <p class="text-gray-500 text-sm" id="fileSize">2.4 MB</p>
120              </div>
121            </div>
122            <button id="removeFileBtn" class="text-red-500 hover:text-red-700">
123              <i class="fas fa-times"></i>
124            </button>
125          </div>
126        </div>
127        
128        <!-- Melody Parameters -->
129        <h2 class="text-xl font-semibold mb-4 mt-8 flex items-center">
130          <i class="fas fa-sliders-h mr-2 text-indigo-500"></i> Melody Parameters
131        </h2>
132        
133        <div class="space-y-6">
134          <div>
135            <label class="block text-sm font-medium text-gray-700 mb-1">Melody Type</label>
136            <div class="grid grid-cols-2 gap-2">
137              <button id="expressiveBtn" class="melody-type-btn bg-indigo-100 text-indigo-700 border-indigo-300">
138                Expressive
139              </button>
140              <button id="simpleBtn" class="melody-type-btn bg-white text-gray-700 border-gray-300">
141                Simple
142              </button>
143            </div>
144          </div>
145          
146          <div>
147            <label for="channelInput" class="block text-sm font-medium text-gray-700 mb-1">Channel</label>
148            <input type="range" id="channelInput" min="0" max="15" value="
1480" class="w-full parameter-slider">
149            <div class="flex justify-between text-xs text-gray-500 mt-1">
150              <span>0</span>
151              <span>15</span>
152            </div>
153          </div>
154          
155          <div>
156            <label for="patchInput" class="block text-sm font-medium text-gray-700 mb-1">Patch (Instrument)</label>
157            <input type="range" id="patchInput" min="0" max="127" value="0" class="w-full parameter-slider">
158            <div class="flex justify-between text-xs text-gray-500 mt-1">
159              <span>0 (Piano)</span>
160              <span>127 (Gunshot)</span>
161            </div>
162          </div>
163          
164          <div>
165            <label for="startChordInput" class="block text-sm font-medium text-gray-700 mb-1">Start Chord</label>
166            <input type="range" id="startChordInput" min="0" max="100" value="0" class="w-full parameter-slider">
167            <div class="flex justify-between text-xs text-gray-500 mt-1">
168              <span>0%</span>
169              <span>100%</span>
170            </div>
171          </div>
172          
173          <div class="flex items-center">
174            <input type="checkbox" id="sustainInput" checked class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
175            <label for="sustainInput" class="ml-2 block text-sm text-gray-700">Apply sustains</label>
176          </div>
177        </div>
178        
179        <button id="generateBtn" class="gradient-bg text-white font-medium py-3 px-6 rounded-lg w-full mt-8 hover:opacity-90 transition flex items-center justify-center">
180          <i class="fas fa-magic mr-2"></i> Generate Melody
181        </button>
182      </section>
183      
184      <!-- Results Section (Always at the bottom) -->
185      <section class="bg-white rounded-xl p-6 shadow-md">
186        <h2 class="text-xl font-semibold mb-4 flex items-center">
187          <i class="fas fa-poll mr-2 text-indigo-500"></i> Results
188        </h2>
189        
190        <div id="emptyState" class="text-center py-12">
191          <i class="fas fa-music text-4xl text-gray-300 mb-4"></i>
192          <p class="text-gray-500">Your generated melody will appear here</p>
193        </div>
194        
195        <div id="loadingState" class="hidden text-center py-12">
196          <div class="loading-spinner inline-block border-4 border-indigo-500 border-t-transparent rounded-full w-12 h-12 mb-4"></div>
197          <p class="text-gray-700">Generating your melody...</p>
198        </div>
199        
200        <div id="resultContainer" class="hidden">
201          <div class="result-card bg-indigo-50 rounded-lg p-4 mb-4">
202            <div class="flex items-center mb-3">
203              <i class="fas fa-file-audio text-indigo-500 mr-2"></i>
204              <h3 class="font-medium" id="resultTitle">Generated Melody</h3>
205            </div>
206            <audio id="audioPlayer" crossOrigin="anonymous" controls class="w-full"></audio>
207            <!-- Audio Visualizer Canvas -->
208            <canvas id="audioVisualizer" class="w-full h-48 mt-4"></canvas>
209            <div class="mt-4 flex space-x-2">
210              <button id="downloadBtn" class="flex-1 bg-white text-indigo-600 border border-indigo-300 py-2 px-4 rounded hover:bg-indigo-50 transition flex items-center justify-center">
211                <i class="fas fa-download mr-2"></i> Download
212              </button>
213              <button id="copyLinkBtn" class="flex-1 bg-white text-indigo-600 border border-indigo-300 py-2 px-4 rounded hover:bg-indigo-50 transition flex items-center justify-center">
214                <i class="fas fa-link mr-2"></i> Copy Link
215              </button>
216            </div>
217          </div>
218          
219          <div class="result-card bg-gray-50 rounded-lg p-4 mt-6">
220            <h3 class="font-medium mb-3 flex items-center">
221              <i class="fas fa-info-circle text-indigo-500 mr-2"></i> Generation Details
222            </h3>
223            <div class="space-y-2 text-sm">
224              <div class="flex justify-between">
225                <span class="text-gray-600">Melody Type:</span>
226                <span id="detailType" class="font-medium">Expressive</span>
227              </div>
228              <div class="flex justify-between">
229                <span class="text-gray-600">Channel:</span>
230                <span id="detailChannel" class="font-medium">0</span>
231              </div>
232              <div class="flex justify-between">
233                <span class="text-gray-600">Instrument:</span>
234                <span id="detailPatch" class="font-medium">Piano (0)</span>
235              </div>
236              <div class="flex justify-between">
237                <span class="text-gray-600">Start Chord:</span>
238                <span id="detailStart" class="font-medium">0%</span>
239              </div>
240              <div class="flex justify-between">
241                <span class="text-gray-600">Sustains:</span>
242                <span id="detailSustain" class="font-medium">Enabled</span>
243              </div>
244            </div>
245            <div id="outputSummary" class="mt-4 text-sm text-gray-700"></div>
246          </div>
247        </div>
248      </section>
249    </main>
250    
251    <!-- Footer -->
252    <footer class="mt-12 text-center text-gray-500 text-sm">
253      <p>Powered by Hugging Face Spaces • MIDI Melody Generator</p>
254      <p class="mt-1">Upload your MIDI file to generate beautiful melodies with AI</p>
255    </footer>
256  </div>
257
258  <!-- Module Script -->
259  
259<script type="module">
260    import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
261    
262    document.addEventListener('DOMContentLoaded', async function() {
263      // DOM Elements
264      const fileDropArea = document.getElementById('fileDropArea');
265      const fileInput = document.getElementById('fileInput');
266      const fileNameDisplay = document.getElementById('fileNameDisplay');
267      const fileName = document.getElementById('fileName');
268      const fileSize = document.getElementById('fileSize');
269      const removeFileBtn = document.getElementById('removeFileBtn');
270      const generateBtn = document.getElementById('generateBtn');
271      const emptyState = document.getElementById('emptyState');
272      const loadingState = document.getElementById('loadingState');
273      const resultContainer = document.getElementById('resultContainer');
274      const audioPlayer = document.getElementById('audioPlayer');
275      const downloadBtn = document.getElementById('downloadBtn');
276      const copyLinkBtn = document.getElementById('copyLinkBtn');
277      const expressiveBtn = document.getElementById('expressiveBtn');
278      const simpleBtn = document.getElementById('simpleBtn');
279      const channelInput = document.getElementById('channelInput');
280      const patchInput = document.getElementById('patchInput');
281      const startChordInput = document.getElementById('startChordInput');
282      const sustainInput = document.getElementById('sustainInput');
283      const detailType = document.getElementById('detailType');
284      const detailChannel = document.getElementById('detailChannel');
285      const detailPatch = document.getElementById('detailPatch');
286      const detailStart = document.getElementById('detailStart');
287      const detailSustain = document.getElementById('detailSustain');
288      const resultTitle = document.getElementById('resultTitle');
289      const outputSummary = document.getElementById('outputSummary');
290      const audioVisualizer = document.getElementById('audioVisualizer');
291      
292      let selectedFile = null;
293      let melodyType = 'Expressive';
294      let generatedMidiFileUrl = null;
295      
296      // Connect to the Gradio backend
297      const gradioClient = await Client.connect("asigalov61/MIDI-Melody");
298      
299      // Event Listeners
300      fileDropArea.addEventListener('click', () => fileInput.click());
301      fileInput.addEventListener('change', handleFileSelect);
302      removeFileBtn.addEventListener('click', removeFile);
303      generateBtn.addEventListener('click', generateMelody);
304      expressiveBtn.addEventListener('click', () => setMelodyType('Expressive'));
305      simpleBtn.addEventListener('click', () => setMelodyType('Simple'));
306      downloadBtn.addEventListener('click', downloadResult);
307      copyLinkBtn.addEventListener('click', copyResultLink);
308      
309      // Drag and drop events
310      ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
311        fileDropArea.addEventListener(eventName, preventDefaults, false);
312      });
313      
314      ['dragenter', 'dragover'].forEach(eventName => {
315        fileDropArea.addEventListener(eventName, highlight, false);
316      });
317      
318      ['dragleave', 'drop'].forEach(eventName => {
319        fileDropArea.addEventListener(eventName, unhighlight, false);
320      });
321      
322      fileDropArea.addEventListener('drop', handleDrop, false);
323      
324      // Parameter change events
325      channelInput.addEventListener('input', updateDetails);
326      patchInput.addEventListener('input', updateDetails);
327      startChordInput.addEventListener('input', updateDetails);
328      sustainInput.addEventListener('change', updateDetails);
329      
330      // Audio visualizer variables
331      let audioCtx, analyser, source;
332      const canvasCtx = audioVisualizer.getContext("2d");
333      
334      function preventDefaults(e) {
335        e.preventDefault();
336        e.stopPropagation();
337      }
338      
339      function highlight() {
340        fileDropArea.classList.add('dragover');
341      }
342      
343      function unhighlight() {
344        fileDropArea.classList.remove('dragover');
345      }
346      
347      function handleDrop(e) {
348        const dt = e.dataTransfer;
349        const files = dt.files;
350        if (files.length) {
351          handleFile(files[0]);
352        }
353      }
354      
355      function handleFileSelect(e) {
356        const files = e.target.files;
357        if (files.length) {
358          handleFile(files[0]);
359        }
360      }
361      
362      function handleFile(file) {
363        if (!file.name.match(/\.(mid|midi)$/i)) {
364          alert('Please upload a MIDI file (.mid or .midi)');
365          return;
366        }
367        selectedFile = file;
368        fileName.textContent = file.name;
369        fileSize.textContent = formatFileSize(file.size);
370        fileNameDisplay.classList.remove('hidden');
371        
372        generateBtn.classList.remove('opacity-50');
373        generateBtn.disabled = false;
374      }
375      
376      function removeFile() {
377        selectedFile = null;
378        fileInput.value = '';
379        fileNameDisplay.classList.add('hidden');
380        generateBtn.classList.add('opacity-50');
381        generateBtn.disabled = true;
382      }
383      
384      function formatFileSize(bytes) {
385        if (bytes === 0) return '0 Bytes';
386        const k = 1024;
387        const sizes = ['Bytes', 'KB', 'MB', 'GB'];
388        const i = Math.floor(Math.log(bytes) / Math.log(k));
389        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
390      }
391      
392      function setMelodyType(type) {
393        melodyType = type;
394        if (type === 'Expressive') {
395          expressiveBtn.classList.add('bg-indigo-100', 'text-indigo-700', 'border-indigo-300');
396          expressiveBtn.classList.remove('bg-white', 'text-gray-700', 'border-gray-300');
397          simpleBtn.classList.add('bg-white', 'text-gray-700', 'border-gray-300');
398          simpleBtn.classList.remove('bg-indigo-100', 'text-indigo-700', 'border-indigo-300');
399        } else {
400          simpleBtn.classList.add('bg-indigo-100', 'text-indigo-700', 'border-indigo-300');
401          simpleBtn.classList.remove('bg-white', 'text-gray-700', 'border-gray-300');
402          expressiveBtn.classList.add('bg-white', 'text-gray-700', 'border-gray-300');
403          expressiveBtn.classList.remove('bg-indigo-100', 'text-indigo-700', 'border-indigo-300');
404        }
405        updateDetails();
406      }
407      
408      async function generateMelody() {
409        if (!selectedFile) return;
410        
411        emptyState.classList.add('hidden');
412        loadingState.classList.remove('hidden');
413        resultContainer.classList.add('hidden');
414        
415        try {
416          const normalizedName = selectedFile.name;
417          const correctedFile = new File([selectedFile], normalizedName, { 
418            type: "audio/midi", 
419            lastModified: selectedFile.lastModified 
420          });
421          
422          const tempBlob = handle_file(correctedFile);
423          const file_ref = new File(
424            [tempBlob],
425            correctedFile.name,
426            { type: "audio/midi", lastModified: correctedFile.lastModified }
427          );
428          
429          const result = await gradioClient.predict("/AddMelody", {
430            input_midi: file_ref,
431            input_mel_type: melodyType,
432            input_channel: parseInt(channelInput.value),
433            input_patch: parseInt(patchInput.value),
434            input_start_chord: parseInt(startChordInput.value),
435            input_apply_sustains: sustainInput.checked,
436          });
437          
438          const [outputTitle, outputSummaryText, outputMidiFile, outputMidiAudio, outputMidiScorePlot] = result.data;
439          loadingState.classList.add('hidden');
440          resultContainer.classList.remove('hidden');
441          
442          resultTitle.textContent = outputTitle || "Generated Melody";
443          outputSummary.textContent = outputSummaryText || "";
444          
445          if (outputMidiAudio) {
446            let audioUrl;
447            if (typeof outputMidiAudio === "object" && outputMidiAudio.url) {
448              audioUrl = outputMidiAudio.url;
449            } else if (outputMidiAudio instanceof Blob) {
450              audioUrl = URL.createObjectURL(outputMidiAudio);
451            } else if (typeof outputMidiAudio === "string") {
452              audioUrl = outputMidiAudio;
453            } else {
454              console.error("Unexpected type for outputMidiAudio:", outputMidiAudio);
455            }
456            audioPlayer.src = audioUrl;
457          }
458          
459          if (outputMidiFile) {
460            let midiUrl;
461            if (typeof outputMidiFile === "object" && outputMidiFile.url) {
462              midiUrl = outputMidiFile.url;
463            } else if (outputMidiFile instanceof Blob) {
464              midiUrl = URL.createObjectURL(outputMidiFile);
465            } else if (typeof outputMidiFile === "string") {
466              midiUrl = outputMidiFile;
467            } else {
468              console.error("Unexpected type for outputMidiFile:", outputMidiFile);
469            }
470            generatedMidiFileUrl = midiUrl;
471          }
472          
473          let plotImg = document.getElementById("midiScorePlot");
474          if (!plotImg) {
475            plotImg = document.createElement("img");
476            plotImg.id = "midiScorePlot";
477            plotImg.className = "w-full mt-4";
478            resultContainer.appendChild(plotImg);
479          }
480          if (outputMidiScorePlot) {
481            let plotUrl;
482            if (typeof outputMidiScorePlot === "object" && outputMidiScorePlot.plot) {
483              plotUrl = outputMidiScorePlot.plot;
484            } else if (outputMidiScorePlot instanceof Blob) {
485              plotUrl = URL.createObjectURL(outputMidiScorePlot);
486            } else if (typeof outputMidiScorePlot === "string") {
487              plotUrl = outputMidiScorePlot;
488            } else {
489              console.error("Unexpected type for outputMidiScorePlot:", outputMidiScorePlot);
490              return;
491            }
492            plotImg.src = plotUrl;
493          }
494          updateDetails();
495        } catch (error) {
496          console.error("Error generating melody:", error);
497          loadingState.classList.add('hidden');
498          emptyState.classList.remove('hidden');
499          alert("Error generating melody. Please try again.");
500        }
501      }
502      
503      function updateDetails() {
504        detailType.textContent = melodyType;
505        detailChannel.textContent = channelInput.value;
506        
507        const patchNumber = parseInt(patchInput.value);
508        let instrumentName = "Piano";
509        if (patchNumber >= 0 && patchNumber <= 7) instrumentName = "Piano";
510        else if (patchNumber >= 8 && patchNumber <= 15) instrumentName = "Chromatic Percussion";
511        else if (patchNumber >= 16 && patchNumber <= 23) instrumentName = "Organ";
512        else if (patchNumber >= 24 && patchNumber <= 31) instrumentName = "Guitar";
513        else if (patchNumber >= 32 && patchNumber <= 39) instrumentName = "Bass";
514        else if (patchNumber >= 40 && patchNumber <= 47) instrumentName = "Strings";
515        else if (patchNumber >= 48 && patchNumber <= 55) instrumentName = "Ensemble";
516        else if (patchNumber >= 56 && patchNumber <= 63) instrumentName = "Brass";
517        else if (patchNumber >= 64 && patchNumber <= 71) instrumentName = "Reed";
518        else if (patchNumber >= 72 && patchNumber <= 79) instrumentName = "Pipe";
519        else if (patchNumber >= 80 && patchNumber <= 87) instrumentName = "Synth Lead";
520        else if (patchNumber >= 88 && patchNumber <= 95) instrumentName = "Synth Pad";
521        else if (patchNumber >= 96 && patchNumber <= 103) instrumentName = "Synth Effects";
522        else if (patchNumber >= 104 && patchNumber <= 111) instrumentName = "Ethnic";
523        else if (patchNumber >= 112 && patchNumber <= 119) instrumentName = "Percussive";
524        else if (patchNumber >= 120 && patchNumber <= 127) instrumentName = "Sound Effects";
525        
526        detailPatch.textContent = `${instrumentName} (${patchNumber})`;
527        detailStart.textContent = `${startChordInput.value}%`;
528        detailSustain.textContent = sustainInput.checked ? "Enabled" : "Disabled";
529      }
530      
531      function downloadResult() {
532        if (generatedMidiFileUrl) {
533          const a = document.createElement("a");
534          a.href = generatedMidiFileUrl;
535          a.download = resultTitle.textContent || "melody.mid";
536          document.body.appendChild(a);
537          a.click();
538          document.body.removeChild(a);
539        } else {
540          alert("No generated file available for download.");
541        }
542      }
543      
544      function copyResultLink() {
545        if (generatedMidiFileUrl) {
546          navigator.clipboard.writeText(generatedMidiFileUrl)
547            .then(() => alert("Download link copied to clipboard!"))
548            .catch(() => alert("Failed to copy the link."));
549        } else {
550          alert("No generated link available to copy.");
551        }
552      }
553      
554      // Setup the audio visualizer when playback starts
555      audioPlayer.addEventListener('play', () => {
556        if (!audioCtx) {
557          audioCtx = new (window.AudioContext || window.webkitAudioContext)();
558          analyser = audioCtx.createAnalyser();
559          analyser.fftSize = 256;
560          source = audioCtx.createMediaElementSource(audioPlayer);
561          source.connect(analyser);
562          analyser.connect(audioCtx.destination);
563          visualize();
564        }
565      });
566      
567      function visualize() {
568        const bufferLength = analyser.frequencyBinCount;
569        const dataArray = new Uint8Array(bufferLength);
570        
571        function draw() {
572          requestAnimationFrame(draw);
573          analyser.getByteFrequencyData(dataArray);
574          canvasCtx.fillStyle = "#f5f7fa";
575          canvasCtx.fillRect(0, 0, audioVisualizer.width, audioVisualizer.height);
576          
577          const barWidth = (audioVisualizer.width / bufferLength) * 2.5;
578          let barHeight;
579          let x = 0;
580          
581          for(let i = 0; i < bufferLength; i++) {
582            barHeight = dataArray[i];
583            const red = barHeight + 25;
584            const green = 250 - barHeight;
585            const blue = 50;
586            canvasCtx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
587            canvasCtx.fillRect(x, audioVisualizer.height - barHeight/2, barWidth, barHeight/2);
588            x += barWidth + 1;
589          }
590        }
591        draw();
592      }
593      
594      // Initialize details on page load
595      updateDetails();
596    });
597  </script>
597
598</body>
599</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.