cooler spotify embeds with 99.9061% less javascript

7/11/2024


hi this is my first blog post! i suck at writing anything down anything so hopefully talking about random things helps.

this all started when i wanted to display my favorite songs on my /about page, i already have a spotify playlist of them so i could just use the official embed. the problem is that the embed loads 1.96 mb of minified javascript.

spotify embed requests

…and also makes tons of strange requests back to spotify, is extremely loud (with no volume slider!), and preloads some songs. on my old site i just gave in and used the official embed, but after seeing that scraping spotify embeds was possible (thanks l-m!), i tried making my own.

all of that javascript is even more insane considering this was meant to be used on other sites, you’d think the multi-billion $ company would care about performance for a simple embed. but it’s no secret that a lot of the web is getting megabytes of javascript larger, from complex frameworks and overkill libraries just to render mostly static sites. tonsky already has a great article on this.

here they are!

extremely good songs

open in spotify
playlist thumbnail

weathergirl

FLAVOR FOLEY

1

Lawsyn

ayowitty

2

THUNDRRR

Quadeca

3

THAT'S WHY

Quadeca

4

WAGING WAR

Quadeca, Olēka

5

NO QUESTIONS ASKED

Quadeca

6

SLAM PUNK

Che

7

Akibare

Mylta

8

My life is mine alone! (feat. nenne)

Mylta, nenne

9

disarmed

kumosai, Sayako

10

spectrum

icesawder

11

NOWNEVER

Jaron

12

How to Pretend

Lucy Bedroque

13

Knot Me

Lucy Bedroque

14

Sweet Pitcher

Lucy Bedroque

15

Thousand yard stare

kmoe

16

angels in camo

Jane Remover

17

Professional Vengeance

Jane Remover

18

Dreamflasher

Jane Remover

19

Experimental Skin

Jane Remover

20

Psychoboost feat danny brown

Jane Remover, Danny Brown

21

Dancing with your eyes closed

Jane Remover

22

Spider

venturing

23

Believe

venturing

24

alucarda lives!

smiling broadly

25

fake blood recipe

smiling broadly

26

flower bed

defsharp

27

sheaskedwhatmylifeislike

ericdoa

28

Caught up (in circles)

Syzy

29

Pastel Express

Cynax

30

St. Chroma (feat. Daniel Caesar)

Tyler, The Creator, Daniel Caesar

31

let's go home

Jane Remover

32

can you tell?

Jane Remover

33

champ

Jane Remover

34

pretender

Jane Remover

35

kodak moment

Jane Remover

36

movies for guys

Jane Remover

37

misplace

Jane Remover

38

Catch me if you can

tn-shi

39

natural

zeroth

40

mint

Snail's House

41

Emerald Lakeside - Action

はがね, Kitsui Akira

42

back off!!!

Jane Remover, kmoe, juno

43

one more life

Murphmusic, park.

44

Clinozoisite

Ludicin

45

Duhhhhhhhhhhhhhhhhh

underscores

46

Rabbit In The Black Room

Rabbit House

47

String Theocracy - Key Ingredient ver.

Mili

48

My guy (Corporate shuffle)

underscores

49

wizards

xaev, mopearound

50

Everything Goes On

Porter Robinson, League of Legends

51

phobie d’impulsion

glaive

52

PUSH UR T3MPRR

femtanyl

53

Amethyst Aurora

BilliumMoto

54

the now now and never

what is your name?

55

Reverse Nightmare Tower

bye2

56

pop music

Limonène

57

EXACTLY WHY I'M STILL HERE - TURQUOISEDEATH Remix

bunnyprodge, TURQUOISEDEATH

58

even when the sun is dead, will you tell them how hard i tried

glaive

59

Tojita Sekai

Camellia

60

commatose

glass beach

61

200

glass beach

62

until the dawn breaks

Deathbrain

63

dumb party

Internet Girl

64

skinz

8485

65

Algas Danses

seatrus

66

うみのゆき

seatrus

67

Palmy Flakes

seatrus

68

Cloud99 (As Above Mix)

Machine Girl

69

enchanted love

linear ring

70

Midnight Theater

Kano, Nagi Nemoto

71

Mola mola

Marmalade butcher

72

Another Ride

ippo.tsk, Synthesizer V ANRI

73

neon glow

glass beach

74

I Still Miss You

bo en, Tomggg

75

(We Are) Friends

bo en, Winamp Boys

76

Our Time

bo en, PAS TASTA

77

Kansoku-eisei

Nanahira, Camellia

78

GURUGURU

Snail's House

79

About 10 Hours of Doubting Myself

Yem

80

GHOST OF LORELEY

lasah

81

awesome ends with ME and ugly starts with U

c0ncernn

82

DILF repellent

c0ncernn

83

タイニーリトル・アジアンタム

ShibayanRecords

84

lastnaut

Sleeping Pola

85

NekovhParavh

Sad Keyboard Guy, Gardens

86

cool delusions about morphing into a cat

crxw

87

Blooming Afternoon

ミツキヨ

88

you're Nxt -Dreams Remix- (feat. MisoilePunch)

uma, Morimori Atsushi, MisoilePunch♪

89

Tenebre Rosso Sangue (ULTRAKILL Original Game Soundtrack)

Keygen Church

90

Altars of Apostasy

Heaven Pierce Her

91

Charisma Overload

Spacey☆Jester

92

Floccinaucinihilipilification

Marmalade butcher

93

Get Your Wish - Anamanaguchi Remix

Porter Robinson, Anamanaguchi

94

Mirror

Porter Robinson

95

Blossom

Porter Robinson

96

Look at the Sky

Porter Robinson

97

Get Your Wish

Porter Robinson

98

Something Comforting

Porter Robinson

99

Falling Behind

Laufey

100

there's more songs on spotify :)

the now now and never

what is your name?

open in spotify
playlist thumbnail

the track embed was made with 0 JS, and the playlist embed was made with 1.84kb (minified) of JS, that’s 99.9061% smaller than the official embed, and i think it fits my site much better :)

ill get the playlist volume slider done one of these days… (9/14/2024: done!)

how

first, you can just make a fetch request to the normal embed url (either for a song or playlist) pretending to be a browser

const res = await fetch(
	`https://open.spotify.com/embed/playlist/2m2lebj9Lg6Riwfyv7G9AD`,
	{
		headers: {
			"User-Agent":
				"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:124.0) Gecko/20100101 Firefox/124.0",
			Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
			"Accept-Language": "en-US,en;q=0.5",
			"Upgrade-Insecure-Requests": "1",
			"Sec-Fetch-Dest": "iframe",
			"Sec-Fetch-Mode": "navigate",
			"Sec-Fetch-Site": "cross-site",
			Pragma: "no-cache",
			"Cache-Control": "no-cache",
		},
		method: "GET",
	},
);

const html = await res.text();

and in the html response you get back, at the end there’s a magical script tag with the id __NEXT_DATA__ that contains everything you need!

response

const json = html.substring(
	html.lastIndexOf(`type="application/json">`) +
		`type="application/json">`.length,
	html.lastIndexOf(`</script>`),
);

const data: PlaylistEmbedData = JSON.parse(json);

you can make whatever with this data now! i’d highly recommend caching it. you can cache song embed data much more aggressively than playlists cause those shouldn’t change.

it’s possible to make your embeds use 0 JS by using the native <audio /> element, which is what i do for singular song embeds. there’s a pretty big problem when using <audio /> for each song in a playlist embed though, it might take safari 8 seconds to load your site sometimes (not an exaggeration).

yes i did pinpoint the loading times to the hundred <audio /> elements, safari really doesn’t like that rendering that many…

note that you can barely style <audio /> elements and the element looks very different across each browser. because of all that i used a bit of JS with web components for my playlist embed, which gets server side rendered with astro.

source code for scraper, components, and typescript types


have a question/comment? want to say hi?

you can also just contact me on discord, @squisket