Overview
SoundWave is a client-side web application that integrates multiple music sources into a unified player interface. The architecture is built around vanilla JavaScript with a modular file structure that separates concerns between different music sources and their implementations.
File Structure
The application consists of the following core files:
Primary Spotify integration page with embedded iframe player Location: /index.htmlPurpose: Provides Spotify search functionality using an external token server
Spotify download link generator interface Location: /index2.htmlPurpose: Generates direct download links from Spotify URLs with audio playback
Alternative Spotify player implementation Location: /index3.htmlPurpose: Lightweight Spotify search and playback interface
Core JavaScript modules for Invidious/YouTube integration Location: /scripts.js, /back.jsPurpose: Handle YouTube music search via Invidious instances with audio extraction
Global stylesheet with dark theme design Location: /styles.cssPurpose: Consistent Spotify-inspired styling across the application
Core Components
1. Spotify Integration Module
The Spotify integration is split across three HTML implementations, each serving different use cases:
Token Management
let accessToken = '' ;
// Obtener el token al cargar la página
async function getAccessToken () {
const response = await fetch ( 'https://jsnode-ab0e.onrender.com' , { method: 'POST' });
const data = await response . json ();
accessToken = data . access_token ;
}
Reference: index.html:96-103
The token is fetched from an external server (jsnode-ab0e.onrender.com) on page load and stored in memory. This server acts as a proxy to manage Spotify API credentials.
Search Implementation
async function searchSongs () {
const query = document . getElementById ( 'search-query' ). value . trim ();
if ( ! query ) {
alert ( 'Por favor, ingresa un término de búsqueda.' );
return ;
}
const response = await fetch ( `https://jsnode-ab0e.onrender.com/search?query= ${ encodeURIComponent ( query ) } &type=track&accessToken= ${ accessToken } ` );
const data = await response . json ();
const songList = document . getElementById ( 'song-list' );
songList . innerHTML = '' ;
data . tracks . items . forEach ( track => {
const li = document . createElement ( 'li' );
li . textContent = ` ${ track . name } - ${ track . artists . map ( artist => artist . name ). join ( ', ' ) } ` ;
li . dataset . embedUrl = `https://open.spotify.com/embed/track/ ${ track . id } ?utm_source=generator&theme=0` ;
li . addEventListener ( 'click' , () => playTrack ( li . dataset . embedUrl ));
songList . appendChild ( li );
});
}
Reference: index.html:106-126
2. Invidious Integration Module
The Invidious integration provides YouTube music playback through privacy-focused frontends:
Suggestion System with Debouncing
function debounce ( func , wait ) {
let timeout ;
return function ( ... args ) {
clearTimeout ( timeout );
timeout = setTimeout (() => func . apply ( this , args ), wait );
};
}
document . getElementById ( 'song-name' ). addEventListener ( 'input' , debounce ( function () {
const songName = this . value ;
suggestionsContainer . innerHTML = '' ;
if ( songName ) {
originalSearchTerm = songName . toLowerCase ();
fetchSuggestions ( songName );
} else {
clearSuggestionsAndResults ();
}
}, 300 )); // 300 ms delay
Reference: scripts.js:19-40 and back.js:19-40
The debounce function prevents excessive API calls by waiting 300ms after the user stops typing before triggering a search.
Multi-Instance Fallback Strategy
function fetchSuggestions ( songName ) {
const url1 = 'https://inv.nadeko.net/search?q=' + encodeURIComponent ( songName );
const url2 = 'https://yewtu.be/search?q=' + encodeURIComponent ( songName );
fetch ( 'https://api.allorigins.win/raw?url=' + encodeURIComponent ( url1 ))
. then ( response => {
if ( response . ok ) return response . text ();
throw new Error ( 'Network response was not ok.' );
})
. then ( data => {
processSuggestionsData ( data );
})
. catch (() => {
fetch ( 'https://api.allorigins.win/raw?url=' + encodeURIComponent ( url2 ))
. then ( response => {
if ( response . ok ) return response . text ();
throw new Error ( 'Network response was not ok.' );
})
. then ( data => {
processSuggestionsData ( data );
})
. catch ( error => {
console . error ( 'Error al obtener sugerencias:' , error );
});
});
}
Reference: scripts.js:104-134
The application uses multiple Invidious instances (inv.nadeko.net, yewtu.be) with automatic failover. If one instance is down, it falls back to the next.
3. State Management
Global state variables track the current application state:
let selectedVideoId = null ;
let selectedTitle = null ;
let currentSuggestions = [];
let originalSearchTerm = '' ;
let currentGroupIndex = 0 ;
let groupedResults = [];
Reference: scripts.js:5-10
Data Flow
Spotify Search Flow
User Input : User enters search query
Token Retrieval : Access token fetched from external server
API Request : Query sent to token server’s search endpoint
Result Parsing : Track data extracted and formatted
UI Update : Results rendered as clickable list items
Playback : Selected track loads in Spotify embed iframe
Invidious Search Flow
The Invidious integration implements a multi-page search:
async function fetchSearchResults ( songName ) {
const maxPages = 6 ;
let allResults = [];
for ( let page = 1 ; page <= maxPages ; page ++ ) {
const url = `https://yewtu.be/search?q= ${ encodeURIComponent ( songName ) } &page= ${ page } ` ;
const response = await fetch ( 'https://api.allorigins.win/raw?url=' + encodeURIComponent ( url ));
const data = await response . text ();
// Parse and filter results
allResults = allResults . concat ( searchResults );
}
// Group results in blocks of 5
groupedResults = [];
for ( let i = 0 ; i < allResults . length ; i += 5 ) {
groupedResults . push ( allResults . slice ( i , i + 5 ));
}
}
Reference: scripts.js:181-239
Show Result Filtering Logic
Results are filtered based on:
Search term matching : Title must contain the search term or all words from it
Content exclusion : Videos marked as “oficial” or “Official” are filtered out
Relevance : Only videos matching the artist/song criteria are shown
. filter ( result => {
const searchTerm = originalSearchTerm . toLowerCase ();
const songTitle = result . title . toLowerCase ();
const includesSearchTerm = (
songTitle . includes ( searchTerm ) ||
searchTerm . split ( ' ' ). every ( term => songTitle . includes ( term ))
);
const excludesOfficial = ! songTitle . includes ( 'oficial' ) && ! songTitle . includes ( 'official' );
return includesSearchTerm && excludesOfficial ;
})
Reference: scripts.js:206-221
Component Communication
Event-Driven Architecture
The application uses DOM events for component communication:
// Enable autoplay after user interaction
document . addEventListener ( 'click' , enableAutoplay , { once: true });
function enableAutoplay () {
console . log ( 'Interacción del usuario detectada. La reproducción automática está habilitada.' );
}
Reference: scripts.js:12-17
DOM Manipulation Pattern
Results are rendered using DocumentFragment for performance:
function showItems ( items , container , isSuggestion = false ) {
const fragment = document . createDocumentFragment ();
items . forEach ( item => {
const itemElement = document . createElement ( 'div' );
itemElement . textContent = item . title ;
itemElement . classList . add ( 'suggestion' );
itemElement . addEventListener ( 'click' , function () {
// Handle click
});
fragment . appendChild ( itemElement );
});
container . innerHTML = '' ;
container . appendChild ( fragment );
container . style . display = 'block' ;
}
Reference: scripts.js:63-102
Using DocumentFragment minimizes DOM reflows by batching multiple element insertions into a single operation.
Error Handling Strategy
Network Resilience
The application implements multiple fallback mechanisms:
Multiple Invidious Instances : Automatic failover between instances
CORS Proxy : All external requests routed through api.allorigins.win
Graceful Degradation : Error messages displayed without breaking functionality
fetch ( allOriginsUrl )
. then ( response => {
if ( response . ok ) return response . text ();
throw new Error ( 'Network response was not ok.' );
})
. catch ( error => {
console . error ( 'Error fetching source code:' , error );
sourceLinksContainer . textContent = 'No se pudo cargar el código fuente.' ;
findNextVideo ( groupIndex , resultIndex );
});
Reference: scripts.js:291-315
300ms debounce prevents excessive API calls during typing.
Results are grouped in blocks of 5 to reduce initial rendering time.
3. Lazy Audio Loading
Audio sources are only fetched when a user selects a specific track.
4. DOM Fragment Usage
Batch DOM insertions minimize layout recalculations.
Security Considerations
CORS Bypass : The application uses api.allorigins.win as a CORS proxy. This introduces a third-party dependency that proxies all Invidious requests.
Token Exposure : Spotify access tokens are exposed in URL parameters when making search requests. Consider using headers for sensitive data.
Deployment Architecture
The application has a distributed architecture:
┌─────────────────┐
│ Client HTML │
│ (Browser) │
└────────┬────────┘
│
┌────┴────┬──────────────┬────────────┐
│ │ │ │
┌───▼──────┐ ┌▼────────────┐ ┌▼──────────▼┐
│ Spotify │ │ Token Server│ │ Invidious │
│ Embed │ │ (Render) │ │ Instances │
└──────────┘ └─────────────┘ └────────────┘
│
┌─────▼──────┐
│ AllOrigins │
│ Proxy │
└────────────┘
Client : Static HTML/JS/CSS files served directly
Token Server : Hosted on Render.com (jsnode-ab0e.onrender.com)
Invidious : Multiple public instances (inv.nadeko.net, yewtu.be)
CORS Proxy : api.allorigins.win for cross-origin requests
Extension Points
The modular architecture allows for easy extension:
Additional Music Sources : Add new HTML pages following the existing pattern
Alternative Invidious Instances : Update the URL list in fetchSuggestions
Enhanced Filtering : Modify the filter logic in processSuggestionsData
Custom Styling : Override styles in styles.css
Next Steps
API Integration Learn about external API usage and integration patterns
Styling System Explore the CSS architecture and theming system