canvas = {
const svg = d3.select(DOM.svg(width, height)).attr("viewBox", [0, -20, width, height + 20]);
const layoutTree = d3.tree().separation(separation).size([width , height - 20]);
const renderLink = d3.linkVertical().x(d => d.x).y(d => d.y);
const Node = d3.hierarchy.prototype.constructor;
const root = new Node;
const nodes = [root];
const links = [];
const box = { height: 30, width: 40};
layoutTree(root);
let link = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.selectAll(".link");
let node = svg.append("g")
.attr("stroke", "orange")
.attr("stroke-width", 2)
.selectAll(".node");
const interval = d3.interval(() => {
if (nodes.length >= 30) return interval.stop();
const parent = nodes[Math.random() * nodes.length | 0];
const child = Object.assign(new Node, {parent, depth: parent.depth + 1});
if (parent.children) parent.children.push(child);
else parent.children = [child];
nodes.push(child);
links.push({source: parent, target: child});
layoutTree(root);
node = node.data(nodes);
node = node.enter().append("rect")
.attr("x", d => d.x - responsiveWidth(d) / 2)
.attr("y", d => d.y - box.height / 2 )
.attr("width", responsiveWidth)
.attr("height", box.height)
.attr("fill", "white")
.attr("stroke", "black")
.attr("stroke-width", "1")
.merge(node);
link = link.data(links);
link = link.enter().insert("path", ".node")
.attr("class", "link")
.attr("d", d => {
const o = {x: d.source.px, y: d.source.py};
return elbow({source: o, target: o});
})
.merge(link);
const t = svg.transition()
.duration(duration);
link.transition(t)
.attr("d", elbow);
node.transition(t)
.attr("x", d => d.x - responsiveWidth(d) / 2)
.attr("y", d => d.y - box.height / 2)
.attr("width", responsiveWidth)
.attr("height", box.height)
.attr("fill", "white")
.attr("stroke", "black")
.attr("stroke-width", "1")
}, duration);
invalidation.then(() => interval.stop());
return svg.node();
}