Published
Edited
May 27, 2022
Insert cell
Insert cell
Insert cell
Insert cell
update = chart.update(keyframe)
Insert cell
data = FileAttachment("spotify_bar@6.csv").csv({typed: true})
Insert cell
duration = 1000
Insert cell
n = 12
Insert cell
Insert cell
datevalues = Array.from(d3.rollup(data, ([d]) => d.value, d => +d.date, d => d.name))
.map(([date, data]) => [new Date(date), data])
.sort(([a], [b]) => d3.ascending(a, b))
Insert cell
function rank(value) {
const data = Array.from(names, name => ({name, value: value(name)}));
data.sort((a, b) => d3.descending(a.value, b.value));
for (let i = 0; i < data.length; ++i) data[i].rank = Math.min(n, i);
return data;
}
Insert cell
k = 10
Insert cell
keyframes = {
const keyframes = [];
let ka, a, kb, b;
for ([[ka, a], [kb, b]] of d3.pairs(datevalues)) {
for (let i = 0; i < k; ++i) {
const t = i / k;
keyframes.push([
new Date(ka * (1 - t) + kb * t),
rank(name => (a.get(name) || 0) * (1 - t) + (b.get(name) || 0) * t)
]);
}
}
keyframes.push([new Date(kb), rank(name => b.get(name) || 0)]);
return keyframes;
}
Insert cell
function bars(svg) {
let bar = svg.append("g")
.attr("fill-opacity", 0.6)
.selectAll("rect");

return ([date, data], transition) => bar = bar
.data(data.slice(0, n), d => d.name)
.join(
enter => enter.append("rect")
.attr("fill", color)
.attr("height", y.bandwidth())
.attr("x", x(0))
.attr("y", y(n))
.attr("width", d => x(d.value) - x(0)),
update => update,
exit => exit.transition(transition).remove()
.attr("y", y(n))
.attr("width", d => x(d.value) - x(0))
)
.call(bar => bar.transition(transition)
.attr("y", d => y(d.rank))
.attr("width", d => x(d.value) - x(0)));
}
Insert cell
function labels(svg) {
let label = svg.append("g")
.style("font", "bold 12px var(--sans-serif)")
.style("font-variant-numeric", "tabular-nums")
.attr("text-anchor", "end")
.selectAll("text");

return ([date, data], transition) => label = label
.data(data.slice(0, n), d => d.name)
// .join(
// enter => enter.append("text")
// .attr("transform", d => `translate(${x(d.value)},${y(n)})`)
// .attr("y", y.bandwidth() / 2)
// .attr("x", -6)
// .attr("dy", "-0.25em")
// .text(d => d.name)
// .call(text => text.append("tspan")
// .attr("fill-opacity", 0.7)
// .attr("font-weight", "normal")
// .attr("x", -6)
// .attr("dy", "1.15em")),
// )
.join(
// enter => enter.append("text")
// .attr("transform", d => `translate(${x(d.value)},${y(n)})`)
// .attr("y", y.bandwidth() / 2)
// .attr("x", -6)
// .attr("dy", "-0.25em")
// .text(d => d.name)
// .call(text => text.append("tspan")
// .attr("fill-opacity", 0.7)
// .attr("font-weight", "normal")
// .attr("x", -6)
// .attr("dy", "1.15em")
function (enter) {
var g = enter.append("g").attr("transform", d => `translate(${x(d.value)},${y(n)})`);
g.append("svg:image")
.attr('x', -40)
.attr('y', 0)
.attr('width', 40)
.attr('height', 43)
.attr("xlink:href", d => `${artist_to_imageurl[d.name]}`);
g.append("text")
.attr("y", y.bandwidth() / 2)
.attr("x", -50)
.attr("dy", "-0.25em")
.text(d => d.name)
.call(text => text.append("tspan")
.attr("fill-opacity", 0.7)
.attr("font-weight", "normal")
.attr("x", -50)
.attr("dy", "1.15em"));
return g;
},
update => update,
exit => exit.transition(transition).remove()
.attr("transform", d => `translate(${x(d.value)},${y(n)})`)
)
.call(bar => bar.transition(transition)
.attr("transform", d => `translate(${x(d.value)},${y(d.rank)})`)
.call(g => g.select("tspan").tween("text", function(d) {
return textTween(parseNumber(this.textContent), d.value);
})));
}
Insert cell
// var g = categorized.enter().append("g");
// g.append("circle").style("fill", "#ddd");
// g.append("text").text(function(d,i){return d.count});

Insert cell
function textTween(a, b) {
const i = d3.interpolateNumber(a, b);
return function(t) {
this.textContent = formatNumber(i(t));
};
}
Insert cell
parseNumber = string => +string.replace(/,/g, "")
Insert cell
formatNumber = d3.format(",d")
Insert cell
function axis(svg) {
const g = svg.append("g")
.attr("transform", `translate(0,${margin.top})`);

const axis = d3.axisTop(x)
.ticks(width / 160)
.tickSizeOuter(0)
.tickSizeInner(-barSize * (n + y.padding()));

return (_, transition) => {
g.transition(transition).call(axis);
g.select(".tick:first-of-type text").remove();
g.selectAll(".tick:not(:first-of-type) line").attr("stroke", "white");
g.select(".domain").remove();
};
}
Insert cell
function ticker(svg) {
const now = svg.append("text")
.style("font", `bold ${barSize}px var(--sans-serif)`)
.style("font-variant-numeric", "tabular-nums")
.attr("text-anchor", "end")
.attr("x", width - 6)
.attr("y", margin.top + barSize * (n - 0.45))
.attr("dy", "0.32em")
.text(formatDate(keyframes[0][0]));

return ([date], transition) => {
transition.end().then(() => now.text(formatDate(date)));
};
}
Insert cell
formatDate = d3.utcFormat("%Y")
Insert cell
color = {
const scale = d3.scaleOrdinal(d3.schemeTableau10);
if (data.some(d => d.category !== undefined)) {
const categoryByName = new Map(data.map(d => [d.name, d.category]))
scale.domain(categoryByName.values());
return d => scale(categoryByName.get(d.name));
}
return d => scale(d.name);
}
Insert cell
x = d3.scaleLinear([0, 1], [margin.left, width - margin.right])
Insert cell
y = d3.scaleBand()
.domain(d3.range(n + 1))
.rangeRound([margin.top, margin.top + barSize * (n + 1 + 0.1)])
.padding(0.1)
Insert cell
height = margin.top + barSize * n + margin.bottom
Insert cell
barSize = 48
Insert cell
margin = ({top: 16, right: 6, bottom: 6, left: 0})
Insert cell
d3 = require("d3@6")
Insert cell
import {Scrubber} from "@mbostock/scrubber"
Insert cell
artist_to_imageurl = {
const artist_to_imageurl = {
"Bruno Mars": "https://i.pinimg.com/originals/c7/ab/2a/c7ab2a96c6c71a3f83f80085804a8b9c.jpg",
"Taylor Swift": "https://www.drawingskill.com/wp-content/uploads/3/Taylor-Swift-Drawing-High-Quality.jpg",
"Drake": "https://i.pinimg.com/736x/d7/a7/1d/d7a71dbb9adbce8297c227ff10bb6041.jpg",
"Black Eyed Peas": "https://i.pinimg.com/736x/26/32/13/2632133d84a92b96280e6ebc90cd9286--black-eyed-pea-lisa-s.jpg",
"Katy Perry": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/katy-perry-dioptri-art.jpg",
"Rihanna": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/3/rihanna-wilda-wilda.jpg",
"Kesha": "https://i.pinimg.com/originals/40/d8/41/40d841b4a979e29a94b9ca7431281e58.jpg",
"B.o.B": "https://i.pinimg.com/474x/59/11/84/5911848d9cfd8e21a9440e974895d1e2--baby-faces-bobs.jpg",
"Jason Derulo": "https://cmw.net/wp-content/uploads/Jason-Derulo-1080x675.jpg",
"Lady Gaga": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/3/lady-gaga-pop-art-herul-stock.jpg",
"Usher": "https://pbs.twimg.com/media/DDFvspOXsAAsIv4.jpg",
"3OH!3": "https://upload.wikimedia.org/wikipedia/en/b/b6/3OH3%21_album.jpg",
"David Guetta": "https://i.pinimg.com/originals/e6/4b/38/e64b3835132210571378bf144698b132.jpg",
"Chris Brown": "https://render.fineartamerica.com/images/rendered/default/print/8/8/break/images/artworkimages/medium/1/chris-brown-abstract-lance-lucas.jpg",
"Adele": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/adele-love-art.jpg",
"Coldplay": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/3/coldplay-1-pop-art-ahmad-nusyirwan.jpg",
"LMFAO": "https://www.drawingskill.com/wp-content/uploads/3/Lmfao-Drawing-Photo.jpg",
"Ed Sheeran": "https://media.newyorker.com/photos/5d31e9fe71b4090008aa0361/1:1/w_929,h_929,c_limit/190729_r34704_rd.jpg",
"Justin Bieber": "https://pbs.twimg.com/media/EFHAxlSW4AEFe1y.jpg",
"Calvin Harris": "https://cdns-images.dzcdn.net/images/artist/5409ae27c19f5008a7b6d724bee189ff/500x500.jpg",
"Lil Wayne": "https://i.redd.it/gtyc2260rfq41.jpg",
"Maroon 5": "https://i.pinimg.com/originals/33/47/68/334768fa4b95de31e0315a436618e671.jpg",
"OneRepublic": "https://i.pinimg.com/736x/fc/9b/2f/fc9b2fa51929bce44b7eebf2da0854c6--one-republic-behance.jpg",
"Kendrick Lamar": "https://images.saatchiart.com/saatchi/990423/art/3792584/2862468-HSC00001-7.jpg",
"Lana Del Ray": "https://i.pinimg.com/originals/ba/08/63/ba08631637df84b30a002c77e958e976.jpg",
"Nicki Minaj": "https://i.icanvas.com/DCA67?d=2&sh=v&p=1&bg=g&t=1649178483",
"Phillip Phillips": "https://mir-s3-cdn-cf.behance.net/project_modules/1400/9cd1eb78869493.5cb12a1ceffc2.jpg",
"Sam Smith": "https://64.media.tumblr.com/3ba8f81f55380a05ec7846e66a09605f/tumblr_nppgarOQir1uwabgxo1_1280.jpg",
"Rae Sremmurd": "https://thefader-res.cloudinary.com/private_images/w_640,c_limit,f_auto,q_auto:eco/rae_sremmurd_sremmlife2_d0vjh4/rae-sremmurd-sremmlife-2-cover-story-interview.jpg",
"Travis Scott": "https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/f825a373899423.5c1938c49dfdb.jpg",
"will.i.am": "https://www.thefamouspeople.com/profiles/images/will-i-am-1.jpg",
"Train": "https://images.fanart.tv/fanart/train-537662cb12ff3.jpg",
"Tinie Tempah": "https://images.saatchiart.com/saatchi/1530645/art/8448147/7512314-HSC00002-7.jpg",
"ScHoolboy Q": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/schoolboy-q-pop-stylised-art-poster-kim-wang.jpg",
"Marshmello": "https://edm.com/.image/ar_1:1%2Cc_fill%2Ccs_srgb%2Cfl_progressive%2Cq_auto:good%2Cw_1200/MTYzOTQ2OTE1ODI4ODY4ODMz/03-marshmello-2016-press-cr-bellnjerry-billboard-1548.jpg",
"Lorde": "https://pbs.twimg.com/media/C6s1kcnWcAEOUBh.jpg",
"Migos": "https://static.displate.com/857x1200/displate/2019-02-19/afe99431c8cde5187e1e587bfe84a083_ce02b0570a65999893b7735b8afbc0af.jpg",
"Paramore": "https://i.pinimg.com/originals/a5/58/a0/a558a064058331b39321a963645395c5.jpg",
"Shawn Mendes": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/2/shawn-mendes-portrait-avarvari-daria.jpg",
"XXXTENTACION": "https://i.etsystatic.com/6934496/r/il/40538d/3212886555/il_fullxfull.3212886555_gvb3.jpg",
"Wiz Khalifa": "https://wallpaperaccess.com/full/125325.jpg",
"Charlie Puth": "https://i.pinimg.com/originals/b8/eb/f1/b8ebf1f51601a8a14185228633918eae.jpg",
"Billie Eilish": "https://i.pinimg.com/474x/f0/4e/d2/f04ed2312f7d19e1346eb236ccf75e9d.jpg",
"Trey Songz": "https://images.artwanted.com/large/33/40140_728933.jpg",
"The 1975": "https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/64144791600805.5e361b4297cbc.jpg",
"Major Lazer": "https://i.pinimg.com/originals/5b/6f/cb/5b6fcbecef7b244e1beace29fe7f69f4.jpg",
"The Chainsmokers": "https://static.displate.com/857x1200/displate/2020-11-30/0ff25ea6882b8661065336bc99d90c22_f39397dee2c9c7e35ab715ba420b4c87.jpg",
"Lil Nas X": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/3/lil-nas-x-muhamad-arif.jpg",
"One Direction": "https://live.staticflickr.com/5476/12285130633_926cdae528_c.jpg",
"X Ambassadors": "https://arc-anglerfish-arc2-prod-advancelocal.s3.amazonaws.com/public/WOFUAEC3TFAVNOITGBFVEWLH6Y.jpg",
"The Vamps": "http://s2.thingpic.com/images/8m/27McKfd8LkGsEx12W5nqoHjo.jpeg",
"Ariana Grande": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/3/ariana-grande-watercolor-portrait-mihaela-pater.jpg",
"Post Malone": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/3/post-malone-asdar-agatha.jpg",
"Selena Gomez & The Scene": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/2/selena-gomez-portrait-2-nenad-vasic.jpg",
"Selena Gomez": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/2/selena-gomez-portrait-2-nenad-vasic.jpg",
"Tyga": "https://64.media.tumblr.com/c1c30579705378d6f33b465a903230e7/tumblr_nx94biwJTA1ukgsm7o1_1280.jpg",
"Pitbull": "https://i.pinimg.com/originals/21/e7/8c/21e78c4a11dcf487ea65a81f44442046.png",
"Sia": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/sia-portrait-yury-malkov.jpg",
"Kygo": "https://static.displate.com/857x1200/displate/2020-03-10/b0dba3d7feb5b5ba109852ef5c35d31f_b07d3170aeac6c2043bbd9b62306edf2.jpg",
"Fetty Wap": "https://img.wattpad.com/cover/46090876-256-k308826.jpg",
"The Weeknd": "https://d3rf6j5nx5r04a.cloudfront.net/3q19_hkPM_aujLcXKy-qARnX8lQ=/product/c/2/54b9ec9498eb44c6bfe9785ead382cc0_opt.jpg",
"Tiësto": "https://i.pinimg.com/564x/d5/5b/3a/d55b3a0b2c1d6c4352cb204fc82bdb16.jpg",
"Lizzo": "https://static01.nyt.com/images/2019/09/10/fashion/04SKIN-LIZZO2/merlin_159278505_bed0b964-1dd6-4f8c-9ad3-8f0984415163-superJumbo.jpg",
"iann dior": "https://i.pinimg.com/originals/06/7b/71/067b71240db450e6fd109cf9ca09cb79.jpg",
"Tove Lo": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/2/tove-lo-russ-carts.jpg",
"fun.": "https://static.displate.com/857x1200/displate/2019-01-06/993f7ac1757c3ee9701d83c933af0e6e_1c4c8b611d337158927ee7027d2b3c53.jpg",
"Imagine Dragons": "https://images.saatchiart.com/saatchi/1142019/art/8115869/7182437-BSMVLAIT-7.jpg",
"Kodaline": "https://c4.wallpaperflare.com/wallpaper/816/553/265/kodaline-vinny-may-jason-boland-top-music-artist-and-bands-wallpaper-thumb.jpg",
"Macklemore & Ryan Lewis": "https://media.newyorker.com/photos/59097472c14b3c606c1085f4/master/pass/160307_r27758.jpg",
"Timbaland": "https://www.theaudiodb.com/images/media/artist/thumb/uqvysq1345460742.jpg",
"Cardi B": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/cardi-b-love-art.jpg",
"Britney Spears": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/3/britney-spears-watercolor-portrait-mihaela-pater.jpg",
"Beyoncé": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/1-beyonce-the-digartist.jpg",
"Lana Del Rey": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/-lana-del-rey-portrait-a-andre-drauflos.jpg",
"Avicii": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/2-avicii-love-art.jpg",
"Justin Timberlake": "https://i.pinimg.com/736x/7c/01/e0/7c01e0bfa20e85d0e00d82793b29b604.jpg",
"Eminem": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/2/eminem-painting-portrait-of-eminem-rapper-artista-fratta.jpg",
"Arctic Monkeys": "https://i.pinimg.com/564x/98/37/23/983723895847f6a9b84a43836bae0497.jpg",
"George Ezra": "https://render.fineartamerica.com/images/images-profile-flow/400/images/artworkimages/mediumlarge/3/music-ezra-george-hervey-dopson.jpg",
"DJ Snake": "https://i.pinimg.com/originals/bc/5b/34/bc5b3489c3722d5cdb750e9cb460c4ff.png",
"Ellie Goulding": "https://mir-s3-cdn-cf.behance.net/project_modules/disp/65d25b23859959.5632a0165ab54.jpg",
"Flo Rida": "https://m.media-amazon.com/images/I/71+ahuQ8paL._AC_SX466_.jpg",
"Bryson Tiller": "https://media.newyorker.com/photos/61b280202ea3314fd81df3c5//w_660,h_1050,c_limit/211220_r39568.jpg",
"Khalid": "https://img.artpal.com/899511/2-16-48t.jpg",
"Anuel AA": "https://i.pinimg.com/736x/ae/ba/3c/aeba3c43a3ed297f778a66bbd9799345.jpg",
"DJ Khaled": "https://images.fineartamerica.com/images/artworkimages/mediumlarge/3/dj-khaled-carl-gouveia.jpg",
"G-Eazy": "https://images.saatchiart.com/saatchi/893711/art/3390952/2460839-HSC00001-7.jpg",
"A Boogie Wit da Hoodie": "https://img.cdn-pictorem.com/uploads/gallerysmall/gallerysmall_311341.jpg",
};
return artist_to_imageurl;
}

Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more