diff --git a/src/player.js b/src/player.js
index 7ef5522..fae3055 100644
--- a/src/player.js
+++ b/src/player.js
@@ -356,14 +356,15 @@ let _playGen = 0;
// from the previous queue can detect they're stale and bail out.
let _queueGen = 0;
-export function playQueue(songs, startIdx) {
+export function playQueue(songs, startIdx, opts = {}) {
_queueGen++;
_radioFilling = false; // cancel any in-flight radio fill for the old queue
S._origQueue = null;
_histClear();
// Shuffle: fully random order, no pinning — startIdx is ignored.
// Both web and Android traverse this fixed shuffled order sequentially.
- if (S.shuffle && songs.length > 1) {
+ // opts.noShuffle = true skips shuffle (e.g. clicking a specific song on the artist page).
+ if (S.shuffle && songs.length > 1 && !opts.noShuffle) {
S._origQueue = songs.map(s => ({...s}));
S.queue = [...songs].sort(() => Math.random() - 0.5).map(s => ({...s}));
S.qIdx = 0;
diff --git a/src/views.js b/src/views.js
index 0cbf3f1..374f40d 100644
--- a/src/views.js
+++ b/src/views.js
@@ -346,7 +346,7 @@ function _makeTopsongRow(s, num, subtitle, list, idx) {
`;
- row.onclick = () => playQueue(list, idx);
+ row.onclick = () => playQueue(list, idx, { noShuffle: true });
row.addEventListener('contextmenu', e => { e.preventDefault(); showCtxMenu(e, s, list, idx); });
const btns = row.querySelectorAll('.icon-btn');
btns[0].onclick = e => { e.stopPropagation(); toggleStar(s, btns[0]); };