Skip to content

Instantly share code, notes, and snippets.

@Slluxx
Last active July 31, 2025 21:18
Show Gist options
  • Save Slluxx/af1afbf96dbb0e42c2bac1949b4dce17 to your computer and use it in GitHub Desktop.
Save Slluxx/af1afbf96dbb0e42c2bac1949b4dce17 to your computer and use it in GitHub Desktop.
Spotify Desk2Mobile
// ==UserScript==
// @name Spotify Desk2Mobile
// @namespace https://github.com/Slluxx/Desk2Mobile
// @downloadURL https://gist.github.com/Slluxx/af1afbf96dbb0e42c2bac1949b4dce17/raw/spotifydesk2mobile.user.js
// @updateURL https://gist.github.com/Slluxx/af1afbf96dbb0e42c2bac1949b4dce17/raw/spotifydesk2mobile.user.js
// @version 2025-07-31.3
// @description Turns the desktop-mode website back into mobile to restore a fully working webplayer.
// @author SLX
// @match https://open.spotify.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=spotify.com
// @grant none
// ==/UserScript==
/*
// Spotify’s web player doesn’t play nice with Android - it pushes you toward
// the app and makes the browser version nearly unusable. Even switching to desktop mode is a mess.
// This simple Tampermonkey/Greasemonkey script fixes that by transforming the desktop view back
// into a mobile-friendly layout without losing any features. You’ll need a browser that supports
// extensions (like Firefox), and Tampermonkey or Greasemonkey installed.
// DISCORD: https://discord.gg/DDnGpsh6
*/
(function () {
'use strict';
function isMobile() {
const regex = /Mobi|Mobile|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
return regex.test(navigator.userAgent);
}
const toggleFullscreen = async () => {
if (!document.fullscreenElement) {
try {
await document.documentElement.requestFullscreen();
} catch (err) {
console.error(err);
}
let elm = container.querySelector('#fullscreenmsg');
elm.classList.add("hidden");
} else {
document.exitFullscreen().catch(console.error);
}
};
let wakeLock = null;
const toggleOled = async () => {
const overlay = document.querySelector('#oledOverlay');
if (overlay.classList.contains("hidden") == false) {
overlay.classList.add("hidden");
if (wakeLock) {
try {
await wakeLock.release();
wakeLock = null;
console.log("Wake lock released");
} catch (err) {
console.error("Wake lock release failed:", err);
}
}
} else {
overlay.classList.remove("hidden");
try {
wakeLock = await navigator.wakeLock.request("screen");
console.log("Wake lock acquired");
} catch (err) {
console.error("Wake lock request failed:", err);
}
}
};
// Inject styles
const style = document.createElement('style');
style.textContent = `
#oledOverlay {
position: absolute;
width: 100vw;
height: 100vh;
background-color: black;
z-index: 99999;
}
/* Non-Desktop-Alert */
.nonDesktopAlert {
background-color: #000000;
position: fixed;
width: 100vw;
height: 100vh;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
flex-direction: column;
box-shadow: inset 0px 0px 105px teal;
}
.nonDesktopAlert > p {
width: 70vw;
color: white;
font-size: larger;
}
.hidden {
display: none !important;
}
#fullscreenmsg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.95);
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 6vw;
z-index: 99999;
text-align: center;
padding: 5vw;
box-sizing: border-box;
box-shadow: inset 0px 0px 105px teal;
}
#fullscreenmsg button {
margin: 4vw;
padding: 3vw 6vw;
font-size: 5vw;
border: none;
border-radius: 1.5vw;
background-color: teal;
color: white;
cursor: pointer;
transition: background 0.3s;
}
#fullscreenmsg button:hover {
background-color: darkcyan;
}
/* destop to phone adjustments */
/* slight margin to look better */
#global-nav-bar {
margin-bottom: 0.5rem;
margin-top: 0.5rem;
}
/* hide spotify logo */
#global-nav-bar > div:nth-of-type(1) > a {
display: none!important;
}
/* hide upgrade button */
[data-testid="upgrade-button"] {
display: none!important;
}
/* hide download button */
[href="/download"] {
display: none!important;
}
/* hide friend activity button */
[data-testid="friend-activity-button"] {
display: none!important;
}
/*
context menu is shared by multiple actions.
it removes the "open in app" button aswell as a spacer div to change the view.
hacky workaround.
*/
#context-menu > ul > :last-child {
display: none!important;
}
/* hide account dropdown items */
[data-testid="user-widget-menu"] > ul > li:nth-child(8),
[data-testid="user-widget-menu"] > ul > li:nth-child(9),
[data-testid="user-widget-menu"] > ul > li:nth-child(10) {
display: none!important;
}
body {
min-width: unset!important;
}
#main > div {
--panel-gap: 0px!important;
}
/* hide left sidebar */
#Desktop_LeftSidebar_Id {
display: none;
}
/* hide right sidebar */
#main > div > div:nth-of-type(2) > div:nth-of-type(7) {
display: none!important;
}
/* remove min width of playbar */
[data-testid="now-playing-bar"] {
min-width: unset!important;
}
/* left side player details */
[data-testid="now-playing-bar"] > div > div:nth-of-type(1) {
display: none;
}
/* right side player buttons */
[data-testid="now-playing-bar"] > div > div:nth-of-type(3) {
display: none;
}
/* fix width of playing bar*/
[data-testid="now-playing-bar"] > div > div:nth-of-type(2) {
max-width: unset!important;
width: 90%!important;
}
/* fix space around playing bar*/
[data-testid="now-playing-bar"] > div {
justify-content: space-around!important;
}
/* remove footer */
[data-testid="footer-div"] {
display: none;
}
/* create better spacing for songs
[data-testid="tracklist-row"] {
grid-template-columns: [index] var(--tracklist-index-column-width,5px) [first] minmax(5px,var(--col1,4fr)) [last] minmax(50px,var(--col2,1fr))!important;
} */
/* remove image cover if it exists */
[data-testid="tracklist-row"] > div:nth-child(2) > :not(:last-child) {
display: none;
}
/* remove add/remove quick button */
[data-testid="tracklist-row"] > div:last-child > :first-child {
display: none;
}
`;
document.head.appendChild(style);
// Create container and set innerHTML
const container = document.createElement('div');
container.id = "spotyclean"
container.innerHTML = `
<div id="oledOverlay" class="hidden"></div>
<div id="nonDesktopAlert" class="nonDesktopAlert hidden"><p>To use Spotify Desk2Mobile, you have to enable desktop mode for this website.</p><p>Once you enable desktop mode, make sure to zoom out all the way!</p></div>
<div id="fullscreenmsg" class="hidden">
<p>Enable fullscreen? <br/> (You should)</p>
<div>
<button id="fs-yes">Yes</button>
<button id="fs-no">No</button>
</div>
</div>
`;
document.body.appendChild(container);
// if on mobile, we need to display the alert and stop everything
if (isMobile()) {
const nonDesktopAlert = container.querySelector('#nonDesktopAlert');
nonDesktopAlert.classList.remove("hidden");
return;
}
// if not in fullscreen, we need to display a message
if (!document.fullscreenElement) {
const fsmsg = container.querySelector('#fullscreenmsg');
fsmsg.classList.remove("hidden");
}
/* Click and unclick the searchbar to extend it */
const observer = new MutationObserver((mutations, obs) => {
const form = document.querySelector('[data-encore-id="formInputIcon"]');
if (form) {
form.click();
if (document.activeElement) {
document.activeElement.blur();
}
obs.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
/* Wait for user-widget-menu and add out own buttons*/
const observer2 = new MutationObserver((mutations, obs) => {
const menu = document.querySelector('[data-testid="user-widget-menu"]');
if (menu && menu.firstChild && menu.firstChild.childNodes.length > 1 && menu.firstChild.firstChild.id != "fsbtn") {
const ul = menu.firstChild
const spacer = ul.querySelector(":scope > div").cloneNode(true)
const li = ul.firstChild
const newLiFullscreen = li.cloneNode(true);
const newLiOled = li.cloneNode(true);
const newLiUpdate = li.cloneNode(true);
newLiFullscreen.firstChild.lastChild.innerHTML = ""
newLiOled.firstChild.lastChild.innerHTML = ""
newLiFullscreen.firstChild.firstChild.innerHTML = "Toggle fullscreen"
newLiOled.firstChild.firstChild.innerHTML = "Wakelock + OLED"
newLiUpdate.firstChild.firstChild.innerHTML = "Update Spotify D2M"
newLiFullscreen.id = "fsbtn";
newLiFullscreen.addEventListener('click', toggleFullscreen);
newLiOled.addEventListener('click', toggleOled);
newLiUpdate.addEventListener('click', function(){
window.open("https://gist.github.com/Slluxx/af1afbf96dbb0e42c2bac1949b4dce17/raw/spotifydesk2mobile.user.js", "_blank");
});
ul.insertBefore(spacer.cloneNode(true), ul.firstChild);
ul.insertBefore(newLiUpdate, ul.firstChild);
ul.insertBefore(spacer, ul.firstChild);
ul.insertBefore(newLiOled, ul.firstChild);
ul.insertBefore(newLiFullscreen, ul.firstChild);
console.log("buttons added");
}
});
observer2.observe(document.body, {
childList: true,
subtree: true
});
document.getElementById("fs-yes").addEventListener("click", function () {
const fsmsg = container.querySelector('#fullscreenmsg');
fsmsg.classList.add("hidden");
try {
document.documentElement.requestFullscreen();
} catch (err) {
console.error(err);
}
})
document.getElementById("fs-no").addEventListener("click", function () {
const fsmsg = container.querySelector('#fullscreenmsg');
fsmsg.classList.add("hidden");
})
const overlay = container.querySelector('#oledOverlay');
overlay.onclick = toggleOled;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment