Skip to content

Instantly share code, notes, and snippets.

Last active December 29, 2016 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tophtucker/170914f8196c4c0238d7ef1031eff129 to your computer and use it in GitHub Desktop.
Save tophtucker/170914f8196c4c0238d7ef1031eff129 to your computer and use it in GitHub Desktop.
Line chart scroller

(Scroll or mouse over table.)

It is hard to show a lot of lines at once on a line chart. It's especially hard when the lines are rough and jaggedy and nowhere-differentiable like, say, stock price charts (because in a true random walk it's impossible to disambiguate an intersection — neither line has any "momentum", so as your eye goes past, it's equiprobable that the lines crossed vs. just touched and bounced away). It gets basically illegible after, like, 3 paths. So here's an approach to scrolling through a set of time series inspired by @armollica's lovely 2D/3D scatterplot.

You can imagine a lot of fixes and variations...

  • Don't totally hide other table rows?
  • Fix jumpiness from scrolling-while-mousing?
  • Mouseovers in plot-space as well as table-space?
  • Position labels at path endpoints instead of as table rows?
  • Scroll through different dimensions?
  • Zoom and pan y-axis as you scroll?

I wanna try some kind of analogue to "adaptive resampling" (drawing cruder lines as you add lines to hold the total 'entropy' of the visualization constant, eventually degenerating to a slopegraph of arbitrarily many lines). Also some kind of abstract analogue to (or just application of?) van Wijk Smooth Zooming.

<!DOCTYPE html>
<meta charset="utf-8">
* {
box-sizing: border-box;
html, body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
font-family: helvetica, sans-serif;
.table-wrapper {
position: relative;
height: 100%;
padding-top: 20px;
.table-scroll {
height: 100%;
overflow: scroll;
table {
margin-right: 0;
margin-left: auto;
thead {
background: white;
th > div {
position: absolute;
width: 4em;
text-align: left;
background: white;
padding: 3px;
top: 0;
td {
padding: 3px;
width: 4em;
th:last-child > div {
text-align: right;
svg {
position: absolute;
top: 0;
left: 0;
width: calc(100% - 8.5em);
height: 100%;
pointer-events: none;
path {
fill: none;
stroke-width: 1;
stroke: black;
<div class="table-wrapper">
<div class="table-scroll">
<script src=""></script>
var data = d3.range(100).map(getRandomSeries)
.sort((a,b) => b.price[b.price.length-1].value - a.price[a.price.length-1].value)
var x = d3.scaleTime()
.domain(d3.extent(d3.merge( => d.price)).map(d =>
var y = d3.scaleLinear()
.domain(d3.extent(d3.merge( => d.price)).map(d => d.value)))
.range([innerHeight, 0])
var line = d3.line()
.x(d => x(
.y(d => y(d.value))
var row ="tbody")
.on("mouseenter", function(d,i) {
row.append("td").text(d => d.ticker)
row.append("td").text(d => d3.format(".0%")(d.price[d.price.length-1].value - 1))
var path ="svg")
.attr("d", d => line(d.price))
var scrollScale = d3.scaleLinear()
.domain([0,".table-scroll").node().scrollHeight - (innerHeight - 20)])
var opacityScale = d3.scaleLinear()
.clamp(true)".table-scroll").on("scroll", function() {
function render(index) {
.style("opacity", (d,i) => opacityScale(Math.abs(index - i)))
.style("font-weight", (d,i) => i == index ? 'bold' : 'normal')
.style("opacity", (d,i) => opacityScale(Math.abs(index - i)))
.style("stroke-width", (d,i) => i == index ? 3 : 1)
function getRandomSeries() {
return {
ticker: getRandomTicker(),
price: getRandomTimeSeries(100)
function getRandomTicker() {
var length = Math.ceil(Math.random()*4);
var chars = 'abcdefghijklmnopqrstuvwxyz';
return d3.range(length).map(() => chars[Math.floor(Math.random()*chars.length)].toUpperCase()).join('');
function getRandomTimeSeries(numPoints) {
var data = d3.range(numPoints).map(d => ({
date: d3.interpolateDate(new Date("2000/01/01"), new Date("2016/10/01"))(d/numPoints),
value: undefined
data.forEach(function(d,i,arr) {
if(i==0) {
d.value = 1
} else {
d.value = arr[i-1].value * d3.randomNormal(1, .02)()
return data
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment