A data-driven lava lamp. That’s the D3 library, can’t you tell?
<!DOCTYPE html>
<head>
<title>Data Driven Lava Lamp</title>
</head>
<meta charset="utf-8">
<style>
svg {
height: 1000px;
width: 1000px;
}
</style>
<body>
<div id="viz">
<svg></svg>
</div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js"></script>
<script src="d3-glyphEdge.js" charset="utf-8" type="text/javascript"></script>
<script>
d3.json("sotu.json", dendrogram);
function dendrogram(data) {
data = data;
treeChart = d3.layout.tree();
treeChart.size([500,500])
.children(function(d) {return d["children"]})
var linkGenerator = d3.svg.diagonal()
var filter = d3.select("svg").append("defs").append("filter").attr("id", "gooeyCodeFilter");
filter.append("feGaussianBlur").attr("id", "gaussblurrer").attr("in", "SourceGraphic").attr("stdDeviation", 6).attr("color-interpolation-filters", "sRGB").attr("result", "blur");
filter.append("feColorMatrix").attr("in", "blur").attr("mode", "matrix").attr("values", "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 34 -7").attr("result", "gooey");
var chartG = d3.select("svg")
.append("g")
.style("filter", "url(#gooeyCodeFilter)")
.attr("class", "dendrogram")
.attr("transform", "translate(0,20)");
chartG
.selectAll("g.link")
.data(treeChart.links(treeChart(data)))
.enter().append("g")
.attr("class", "link")
.append("path")
.attr("d", linkGenerator)
.style("fill", "none")
.style("stroke", "lightgray")
.style("stroke-width", "0px")
.each(function (d) {
d.particles = [];
d.frequency = d.target.blockCalls ? particleScale(d.target.blockCalls) : 0;
d.speed = Math.random() + 1;
})
chartG
.selectAll("circle")
.data(treeChart(data))
.enter().append("ellipse")
.attr("rx", 4)
.attr("ry", 2)
.style("stroke-width", 1)
.attr("cx", function (d) {return d.x})
.attr("cy", function (d) {return d.y})
.style("fill", "white")
.style("stroke", "#f5e0b7")
var t = d3.timer(tick, 1000);
function tick(elapsed, time) {
particles();
}
function step(timestamp) {
if (!start) start = timestamp;
// var progress = timestamp - start;
particles();
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
}
var blockScale = d3.scale.linear().domain([1,5,50,1000,3000]).range(["#b9e3c5", "#6f89b6", "#827abf", "#f62150", "#5b1e37"]).clamp(true)
var particleScale = d3.scale.linear().domain([0,3000]).range([0.01,0.05])
function particles() {
d3.selectAll("g.link")
.each(function (d, i) {
d3_glyphEdge.mutate.particle(d, d3.select(this).select("path").node(), 3, d.speed);
d3.select(this).selectAll("circle")
.data(d.particles)
.enter()
.append("circle")
.style("fill", d.target.blockCalls ? blockScale(d.target.blockCalls) : "blue")
.attr("r", 5);
d3.select(this).selectAll("circle")
.data(d.particles)
.exit()
.remove();
d3.select(this).selectAll("circle")
.attr("cx", function (p) {return p.x})
.attr("cy", function (p) {return p.y});
})
}
</script>
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define('d3-glyphEdge', ['exports'], factory) :
factory((global.d3_glyphEdge = {}));
}(this, function (exports) { 'use strict';
function halfArrow(d, nodeTargetSize, bodySize, headSize) {
var diffX = d.target.y - d.source.y;
var diffY = d.target.x - d.source.x;
var headDistance = headSize * 3;
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) );
var angle1 = angle0 - ( Math.PI / 2 );
var angle2 = angle0 + ( Math.PI / 2 );
var x1 = d.source.x + (headSize * Math.cos(angle1));
var y1 = d.source.y - (headSize * Math.sin(angle1));
var x2 = d.source.x + (headSize * Math.cos(angle2));
var y2 = d.source.y - (headSize * Math.sin(angle2));
var x3 = d.target.x - (headSize * Math.cos(angle1));
var y3 = d.target.y + (headSize * Math.sin(angle1));
var mx1 = d.source.x + (bodySize * Math.cos(angle1));
var my1 = d.source.y - (bodySize * Math.sin(angle1));
var mx2 = d.source.x + (bodySize * Math.cos(angle2));
var my2 = d.source.y - (bodySize * Math.sin(angle2));
var mx3 = d.target.x + (bodySize * Math.cos(angle1));
var my3 = d.target.y - (bodySize * Math.sin(angle1));
var dY = d.source.y - d.target.y;
var dX = d.source.x - d.target.x;
var midDiffY1 = my1 - my3;
var midDiffX1 = mx1 - mx3;
var diffY1 = y1 - y3;
var diffX1 = x1 - x3;
var pythag = Math.sqrt((midDiffX1 * midDiffX1) + (midDiffY1 * midDiffY1));
var pythag2 = Math.sqrt((dX * dX) + (dY * dY));
var adjX1 = mx2 - ((midDiffX1 * (pythag - headDistance - nodeTargetSize)) / pythag);
var adjY1 = my2 - ((midDiffY1 * (pythag - headDistance - nodeTargetSize)) / pythag);
var headX1 = x2 - ((diffX1 * (pythag - headDistance - nodeTargetSize)) / pythag);
var headY1 = y2 - ((diffY1 * (pythag - headDistance - nodeTargetSize)) / pythag);
var tipX = d.source.x - ((dX * (pythag2 - nodeTargetSize)) / pythag2);
var tipY = d.source.y - ((dY * (pythag2 - nodeTargetSize)) / pythag2);
return "M" + d.source.x + "," + d.source.y + "L" + mx2 + "," + my2 + "L" + adjX1 + "," + adjY1 + "L" + headX1 + "," + headY1 + "L" + tipX + "," + tipY + "L" + d.source.x + "," + d.source.y + "z";
};
function lineArc(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
};
function ribbon(d, bodySize) {
var diffX = d.target.y - d.source.y;
var diffY = d.target.x - d.source.x;
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) );
var angle1 = angle0 - ( Math.PI / 2 );
var angle2 = angle0 + ( Math.PI / 2 );
var mx1 = d.source.x + (bodySize * Math.cos(angle1));
var my1 = d.source.y - (bodySize * Math.sin(angle1));
var mx2 = d.source.x + (bodySize * Math.cos(angle2));
var my2 = d.source.y - (bodySize * Math.sin(angle2));
var mx3 = d.target.x - (bodySize * Math.cos(angle1));
var my3 = d.target.y + (bodySize * Math.sin(angle1));
var mx4 = d.target.x - (bodySize * Math.cos(angle2));
var my4 = d.target.y + (bodySize * Math.sin(angle2));
return "M" + mx1 + "," + my1 + "L" + mx2 + "," + my2 + "L" + mx3 + "," + my3 + "L" + mx4 + "," + my4 + "z";
}
function taffy(d, nodeSourceSize, nodeTargetSize, midpointSize) {
var diffX = d.target.y - d.source.y;
var diffY = d.target.x - d.source.x;
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) );
var angle1 = angle0 - ( Math.PI / 2 );
var angle2 = angle0 + ( Math.PI / 2 );
var x1 = d.source.x + (nodeSourceSize * Math.cos(angle1));
var y1 = d.source.y - (nodeSourceSize * Math.sin(angle1));
var x2 = d.source.x + (nodeSourceSize * Math.cos(angle2));
var y2 = d.source.y - (nodeSourceSize * Math.sin(angle2));
var x3 = d.target.x + (nodeTargetSize * Math.cos(angle2));
var y3 = d.target.y - (nodeTargetSize * Math.sin(angle2));
var x4 = d.target.x + (nodeTargetSize * Math.cos(angle1));
var y4 = d.target.y - (nodeTargetSize * Math.sin(angle1));
var mx1 = d.source.x + (midpointSize * Math.cos(angle1));
var my1 = d.source.y - (midpointSize * Math.sin(angle1));
var mx2 = d.source.x + (midpointSize * Math.cos(angle2));
var my2 = d.source.y - (midpointSize * Math.sin(angle2));
var mx3 = d.target.x + (midpointSize * Math.cos(angle1));
var my3 = d.target.y - (midpointSize * Math.sin(angle1));
var mx4 = d.target.x + (midpointSize * Math.cos(angle2));
var my4 = d.target.y - (midpointSize * Math.sin(angle2));
var midY2 = (my1 + my3) / 2;
var midX2 = (mx1 + mx3) / 2;
var midY1 = (my2 + my4) / 2;
var midX1 = (mx2 + mx4) / 2;
return "M" + x1 + "," + y1 + "L" + x2 + "," + y2 + " L " + midX1 + "," + midY1 + " L " + x3 + "," + y3 + " L " + x4 + "," + y4 + " L " + midX2 + "," + midY2 + "z";
};
function nail(d, nodeSize) {
var diffX = d.target.y - d.source.y;
var diffY = d.target.x - d.source.x;
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) );
var angle1 = angle0 - ( Math.PI / 2 );
var angle2 = angle0 + ( Math.PI / 2 );
var x1 = d.source.x + (nodeSize * Math.cos(angle1));
var y1 = d.source.y - (nodeSize * Math.sin(angle1));
var x2 = d.source.x + (nodeSize * Math.cos(angle2));
var y2 = d.source.y - (nodeSize * Math.sin(angle2));
return "M" + x1 + "," + y1 + "L" + x2 + "," + y2 + " L " + d.target.x + "," + d.target.y + "z";
};
function comet(d, nodeSize) {
var diffX = d.target.y - d.source.y;
var diffY = d.target.x - d.source.x;
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) );
var angle1 = angle0 - ( Math.PI / 2 );
var angle2 = angle0 + ( Math.PI / 2 );
var x1 = d.target.x + (nodeSize * Math.cos(angle1));
var y1 = d.target.y - (nodeSize * Math.sin(angle1));
var x2 = d.target.x + (nodeSize * Math.cos(angle2));
var y2 = d.target.y - (nodeSize * Math.sin(angle2));
return "M" + x1 + "," + y1 + "L" + x2 + "," + y2 + " L " + d.source.x + "," + d.source.y + "z";
};
function arrowhead(d, nodeTargetSize, bodySize, headSize) {
var diffX = d.target.y - d.source.y;
var diffY = d.target.x - d.source.x;
var headDistance = headSize * 3;
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) );
var angle1 = angle0 - ( Math.PI / 2 );
var angle2 = angle0 + ( Math.PI / 2 );
var x1 = d.source.x + (headSize * Math.cos(angle1));
var y1 = d.source.y - (headSize * Math.sin(angle1));
var x2 = d.source.x + (headSize * Math.cos(angle2));
var y2 = d.source.y - (headSize * Math.sin(angle2));
var x3 = d.target.x - (headSize * Math.cos(angle1));
var y3 = d.target.y + (headSize * Math.sin(angle1));
var x4 = d.target.x - (headSize * Math.cos(angle2));
var y4 = d.target.y + (headSize * Math.sin(angle2));
var mx1 = d.source.x + (bodySize * Math.cos(angle1));
var my1 = d.source.y - (bodySize * Math.sin(angle1));
var mx2 = d.source.x + (bodySize * Math.cos(angle2));
var my2 = d.source.y - (bodySize * Math.sin(angle2));
var mx3 = d.target.x + (bodySize * Math.cos(angle1));
var my3 = d.target.y - (bodySize * Math.sin(angle1));
var mx4 = d.target.x + (bodySize * Math.cos(angle2));
var my4 = d.target.y - (bodySize * Math.sin(angle2));
var dY = d.source.y - d.target.y;
var dX = d.source.x - d.target.x;
var midDiffY1 = my1 - my3;
var midDiffX1 = mx1 - mx3;
var midDiffY2 = my2 - my4;
var midDiffX2 = mx2 - mx4;
var diffY1 = y1 - y3;
var diffX1 = x1 - x3;
var diffY2 = y2 - y4;
var diffX2 = x2 - x4;
var pythag = Math.sqrt((midDiffX1 * midDiffX1) + (midDiffY1 * midDiffY1));
var pythag2 = Math.sqrt((dX * dX) + (dY * dY));
var adjX1 = mx2 - ((midDiffX1 * (pythag - headDistance - nodeTargetSize)) / pythag);
var adjY1 = my2 - ((midDiffY1 * (pythag - headDistance - nodeTargetSize)) / pythag);
var adjX2 = mx1 - ((midDiffX2 * (pythag - headDistance - nodeTargetSize)) / pythag);
var adjY2 = my1 - ((midDiffY2 * (pythag - headDistance - nodeTargetSize)) / pythag);
var headX2 = x1 - ((diffX2 * (pythag - headDistance - nodeTargetSize)) / pythag);
var headY2 = y1 - ((diffY2 * (pythag - headDistance - nodeTargetSize)) / pythag);
var headX1 = x2 - ((diffX1 * (pythag - headDistance - nodeTargetSize)) / pythag);
var headY1 = y2 - ((diffY1 * (pythag - headDistance - nodeTargetSize)) / pythag);
var tipX = d.source.x - ((dX * (pythag2 - nodeTargetSize)) / pythag2);
var tipY = d.source.y - ((dY * (pythag2 - nodeTargetSize)) / pythag2);
return "M" + mx2 + "," + my2 + "L" + adjX1 + "," + adjY1 + "L" + headX1 + "," + headY1 + "L" + tipX + "," + tipY + "L" + headX2 + "," + headY2 + "L" + adjX2 + "," + adjY2 + "L" + mx1 + "," + my1 + "z";
};
function parallel(d, sourceSize, targetSize, edgeNumber) {
var diffX = d.target.y - d.source.y;
var diffY = d.target.x - d.source.x;
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) );
var angle1 = angle0 + ( (Math.PI * 0.75) + (edgeNumber * 0.25) );
var angle2 = angle0 + ( (Math.PI * 0.25) - (edgeNumber * 0.25) );
var x1 = d.source.x + (sourceSize * Math.cos(angle1));
var y1 = d.source.y - (sourceSize * Math.sin(angle1));
var x2 = d.target.x + (targetSize * Math.cos(angle2));
var y2 = d.target.y - (targetSize * Math.sin(angle2));
return {source: {x: x1, y: y1}, target: {x: x2, y: y2}};
}
function offset(d, nodeSize) {
var diffX = d.target.y - d.source.y;
var diffY = d.target.x - d.source.x;
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) );
var angle1 = angle0 + ( Math.PI * 0.75 );
var angle2 = angle0 + ( Math.PI * 0.25 );
var x1 = d.source.x + (nodeSize * Math.cos(angle1));
var y1 = d.source.y - (nodeSize * Math.sin(angle1));
var x2 = d.target.x + (nodeSize * Math.cos(angle2));
var y2 = d.target.y - (nodeSize * Math.sin(angle2));
return {source: {x: x1, y: y1}, target: {x: x2, y: y2}};
}
function particle(d, path, pathWidth, speed) {
pathWidth = pathWidth / 2;
d.particles = d.particles.filter(function (d) {return d.current < path.getTotalLength()});
if (d.frequency < 1) {
if (Math.random() < d.frequency) {
pushParticle();
}
} else {
for (var x = 0; x < d.frequency; x++) {
pushParticle();
}
}
function pushParticle() {
d.particles.push({current: 0, xOffset: pathWidth - (pathWidth * Math.random() * 2), yOffset: pathWidth - (pathWidth * Math.random() * 2)});
}
d.particles.forEach(function (particle) {
particle.current = particle.current + speed;
var currentPosition = path.getPointAtLength(particle.current);
particle.x = currentPosition.x + particle.xOffset;
particle.y = currentPosition.y + particle.yOffset;
});
};
var d = {
arrowhead: arrowhead,
comet: comet,
nail: nail,
taffy: taffy,
ribbon: ribbon,
lineArc: lineArc,
halfArrow: halfArrow
};
var project = {
offset: offset,
parallel: parallel
}
var mutate = {
particle: particle
}
var version = "1.0.0";
exports.version = version;
exports.d = d;
exports.project = project;
exports.mutate = mutate;
}));