Skip to content

Instantly share code, notes, and snippets.

@antonl-dev
Last active July 14, 2025 15:33
Show Gist options
  • Select an option

  • Save antonl-dev/efe9e7c097fd5a9e1f65b0fe4779cc91 to your computer and use it in GitHub Desktop.

Select an option

Save antonl-dev/efe9e7c097fd5a9e1f65b0fe4779cc91 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Song Launchpad</title>
<style>
:root {
--bg-gradient: linear-gradient(135deg, #2c004f, #6b006d, #b51778, #f05454);
--card-bg: rgba(20, 18, 30, 0.8);
--text-color: #f0f0f0;
--accent-color: #e44d87;
--border-color: rgba(255, 255, 255, 0.2);
--shadow-color: rgba(0, 0, 0, 0.5);
--input-bg: rgba(0, 0, 0, 0.3);
}
html, body {
height: 100%;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: var(--text-color);
}
body {
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-gradient);
background-size: 200% 200%;
animation: gradient-animation 15s ease infinite;
padding: 20px;
box-sizing: border-box;
}
@keyframes gradient-animation {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.launchpad {
width: 100%;
max-width: 600px;
background: var(--card-bg);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--border-color);
border-radius: 16px;
padding: 30px;
box-shadow: 0 8px 32px 0 var(--shadow-color);
text-align: center;
}
h1 {
margin-top: 0;
margin-bottom: 10px;
font-weight: 600;
}
p {
margin-top: 0;
margin-bottom: 25px;
opacity: 0.8;
font-size: 0.9em;
}
#song-input {
width: 100%;
min-height: 60px;
padding: 15px;
box-sizing: border-box;
background-color: var(--input-bg);
border: 2px solid var(--border-color);
border-radius: 10px;
color: var(--text-color);
font-size: 1.1em;
resize: vertical;
transition: all 0.3s ease;
}
#song-input:focus, #song-input.drag-over {
border-color: var(--accent-color);
outline: none;
box-shadow: 0 0 15px rgba(228, 77, 135, 0.5);
}
#song-input::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.platforms-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
gap: 15px;
margin-top: 30px;
}
.platform-link {
display: flex;
align-items: center;
justify-content: center;
width: 60px;
height: 60px;
background-color: var(--input-bg);
border: 1px solid var(--border-color);
border-radius: 50%;
text-decoration: none;
transition: all 0.2s ease-in-out;
user-select: none;
/* For fallback text */
color: var(--text-color);
font-size: 1.5em;
font-weight: bold;
}
.platform-link:hover {
/* The --brand-color is set via JS for each button */
background-color: var(--brand-color, var(--accent-color));
border-color: var(--brand-color, var(--accent-color));
transform: scale(1.1);
}
.platform-link img {
width: 55%;
height: 55%;
filter: invert(1); /* Makes icons white */
transition: filter 0.2s ease-in-out;
}
.platform-link:hover img {
filter: invert(0); /* On hover, shows original icon color if it's dark on a light bg */
}
/* Specific filter override for icons that are already light */
.platform-link[data-key="y"]:hover img,
.platform-link[data-key="b"]:hover img,
.platform-link[data-key="c"]:hover img {
filter: invert(1) brightness(1.5);
}
</style>
</head>
<body>
<div class="launchpad">
<h1>Song Launchpad</h1>
<p>Paste or drag & drop a song title below, then click a service to search.</p>
<textarea id="song-input" placeholder="e.g., Run To You - Sam Bird , Papa Zeus"></textarea>
<div id="platforms-container" class="platforms-container">
<!-- Platform links will be generated here by JavaScript -->
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const inputArea = document.getElementById('song-input');
const platformsContainer = document.getElementById('platforms-container');
// Data now includes icon URLs and brand colors
const searchProviders = [
{ name: "YouTube Music", key: "y", base_url: "https://music.youtube.com/search?q=", encoding_style: "query_plus", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/youtubemusic.svg", color: "#FF0000" },
{ name: "Spotify", key: "s", base_url: "https://open.spotify.com/search/", encoding_style: "path_percent", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/spotify.svg", color: "#1DB954" },
{ name: "Apple Music", key: "a", base_url: "https://music.apple.com/us/search?term=", encoding_style: "path_percent", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/apple.svg", color: "#FA57C1" },
{ name: "SoundCloud", key: "c", base_url: "https://soundcloud.com/search?q=", encoding_style: "path_percent", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/soundcloud.svg", color: "#FF5500" },
{ name: "Deezer", key: "d", base_url: "https://www.deezer.com/search/", encoding_style: "path_percent", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/deezer.svg", color: "#FEAA2D" },
{ name: "Bandcamp", key: "b", base_url: "https://bandcamp.com/search?q=", encoding_style: "bandcamp_special", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/bandcamp.svg", color: "#1DA0C3" },
{ name: "DuckDuckGo", key: "w", base_url: "https://duckduckgo.com/?q=", encoding_style: "query_plus", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/duckduckgo.svg", color: "#DE5833" }
];
function encodeSongTitle(title, style) {
switch (style) {
case "query_plus": return encodeURIComponent(title).replace(/%20/g, '+');
case "path_percent": return encodeURIComponent(title);
case "bandcamp_special": return encodeURIComponent(title.replace(/[\s.-]/g, '+'));
default: return encodeURIComponent(title);
}
}
searchProviders.forEach(provider => {
const link = document.createElement('a');
link.className = 'platform-link';
link.href = '#';
link.title = `Search on ${provider.name}`;
link.setAttribute('data-key', provider.key);
// Set the brand color as a CSS custom property for the hover effect
link.style.setProperty('--brand-color', provider.color);
const icon = document.createElement('img');
icon.src = provider.icon_url;
icon.alt = provider.name;
// --- Fallback Mechanism ---
// If the icon fails to load, remove it and show the text key instead.
icon.onerror = function() {
link.textContent = provider.key.toUpperCase();
};
link.appendChild(icon);
link.addEventListener('click', (e) => {
e.preventDefault();
const songTitle = inputArea.value.trim();
if (!songTitle) {
inputArea.focus();
inputArea.placeholder = "Please enter a song title first!";
return;
}
const encodedTitle = encodeSongTitle(songTitle, provider.encoding_style);
const finalUrl = provider.base_url + encodedTitle;
window.open(finalUrl, '_blank');
});
platformsContainer.appendChild(link);
});
// --- Drag and Drop functionality ---
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
inputArea.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation(); }, false);
});
['dragenter', 'dragover'].forEach(eventName => {
inputArea.addEventListener(eventName, () => inputArea.classList.add('drag-over'), false);
});
['dragleave', 'drop'].forEach(eventName => {
inputArea.addEventListener(eventName, () => inputArea.classList.remove('drag-over'), false);
});
inputArea.addEventListener('drop', e => {
const droppedText = e.dataTransfer.getData('text/plain');
if (droppedText) inputArea.value = droppedText;
}, false);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment