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

Unlimited Love

underscores

1

WHITEHORSEMANNEQUIN

Lucy Bedroque

2

keep me going (BIRDBRAIN)

slayr

3

God (with current joys & never goodbye)

matt proxy, Current Joys, never goodbye

4

STARS

matt proxy

5

Burn It

ok, fakemink

6

Trajectory

lapix, Mylta, RANASOL

7

fish in the pool

yeule

8

E7len's Knife

icesawder

9

Music Baby (leroy Remix)

Jane Remover

10

Dyad

Jamie Paige, unit.0

11

Machine Love

Jamie Paige

12

Flashout Freestyle

slayr

13

My Darling, My Companion

Jamie Paige

14

NOGWAPNOLIFE

bleood

15

BIRDBRAIN

Jamie Paige, OK Glass

16

In the dark

venturing

17

Set In Stone

slayr

18

Love Blur

slayr

19

Music Baby

Jane Remover

20

media offline

ariiol

21

SISTERHOOD (LOVE, HER.)

Lucy Bedroque

22

The End

Danny Brown, ta Ukrainka, Zheani, Cynthoni

23

Music and Me

fakemink

24

MIRINAE

TAK, Zekk

25

Rain Capsule

Namitape

26

Overfeel

Namitape

27

All At Once

Ninajirachi

28

Infohazard

Ninajirachi

29

Delete

Ninajirachi

30

weathergirl

FLAVOR FOLEY

31

Lawsyn

ayowitty

32

THUNDRRR

Quadeca

33

THAT'S WHY

Quadeca

34

WAGING WAR

Quadeca, Olēka

35

NO QUESTIONS ASKED

Quadeca

36

Always Have Always Will

Jane Remover

37

SLAM PUNK

Che

38

Akibare

Mylta

39

My life is mine alone! (feat. nenne)

Mylta, nenne

40

disarmed

kumosai, Sayako

41

spectrum

icesawder

42

NOWNEVER

Jaron Steele

43

Pretty Peach

Lucy Bedroque

44

How to Pretend

Lucy Bedroque

45

Knot Me

Lucy Bedroque

46

Sweet Pitcher

Lucy Bedroque

47

Thousand yard stare

kmoe

48

angels in camo

Jane Remover

49

Professional Vengeance

Jane Remover

50

Dreamflasher

Jane Remover

51

Experimental Skin

Jane Remover

52

Psychoboost

Jane Remover, Danny Brown

53

Dancing with your eyes closed

Jane Remover

54

Spider

venturing

55

Believe

venturing

56

alucarda lives!

smiling broadly

57

fake blood recipe

smiling broadly

58

flower bed

defsharp

59

sheaskedwhatmylifeislike

ericdoa

60

Caught up (in circles)

Syzy

61

Pastel Express

Cynax

62

St. Chroma (feat. Daniel Caesar)

Tyler, The Creator, Daniel Caesar

63

let's go home

Jane Remover

64

can you tell?

Jane Remover

65

champ

Jane Remover

66

pretender

Jane Remover

67

kodak moment

Jane Remover

68

movies for guys

Jane Remover

69

misplace

Jane Remover

70

Catch me if you can

tn-shi

71

natural

zeroth

72

mint

Snail's House

73

Emerald Lakeside - Action

はがね, Kitsui Akira

74

back off!!!

Jane Remover, kmoe, juno

75

one more life

Murphmusic, park.

76

Clinozoisite

Ludicin

77

Duhhhhhhhhhhhhhhhhh

underscores

78

Rabbit In The Black Room

Rabbit House

79

String Theocracy - Key Ingredient ver.

Mili

80

My guy (Corporate shuffle)

underscores

81

Wizards

xaev, mopearound

82

Everything Goes On

Porter Robinson, League of Legends

83

phobie d’impulsion

glaive

84

PUSH UR T3MPRR

femtanyl

85

Amethyst Aurora

BilliumMoto

86

the now now and never

what is your name?

87

Reverse Nightmare Tower

bye2

88

pop music

Limonène

89

EXACTLY WHY I'M STILL HERE - TURQUOISEDEATH Remix

bunnyprodge, TURQUOISEDEATH

90

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

glaive

91

Tojita Sekai

Camellia

92

commatose

glass beach

93

200

glass beach

94

until the dawn breaks

Deathbrain

95

dumb party

Internet Girl

96

skinz

8485

97

Algas Danses

seatrus

98

うみのゆき

seatrus

99

Palmy Flakes

seatrus

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