Skip to content

Instantly share code, notes, and snippets.

Last active March 8, 2021 09:31
Show Gist options
  • Save tophtucker/fbfa67d296815227af93bf235d8a9d6f to your computer and use it in GitHub Desktop.
Save tophtucker/fbfa67d296815227af93bf235d8a9d6f to your computer and use it in GitHub Desktop.
Continuous word wrap

Been meaning to try this for a long time! Inspired to go for it by Omar's recent work! I forget how to work with gists!


  • Account for padding and such
  • “Snap” words to the next line where they fit whole
  • Wrap into different containers? Different directions?
  • Ultimate goal: diagonal accelerometer scroll 😈

Sample text is from Moby Dick, Chapter XCVI: The Try-Works

<!DOCTYPE html>
<meta charset="utf-8">
body {
margin: 0;
padding: 0;
span {
display: inline-block;
<script src=""></script>
const text = `Look not too long in the face of the fire, O man! Never dream with thy hand on the helm! Turn not thy back to the compass; accept the first hint of the hitching tiller; believe not the artificial fire, when its redness makes all things look ghastly. Tomorrow, in the natural sun, the skies will be bright; those who glared like devils in the forking flames, the morn will show in far other, at least gentler, relief; the glorious, golden, glad sun, the only true lamp—all others but liars! Nevertheless the sun hides not Virginia’s Dismal Swamp, nor Rome’s accursed Campagna, nor wide Sahara, nor all the millions of miles of deserts and of griefs beneath the moon. The sun hides not the ocean, which is the dark side of this earth, and which is two thirds of this earth. So, therefore, that mortal man who hath more of joy than sorrow in him, that mortal man cannot be true—not true, or undeveloped. With books the same. The truest of all men was the Man of Sorrows, and the truest of all books is Solomon’s, and Ecclesiastes is the fine hammered steel of woe. “All is vanity”. ALL. This wilful world hath not got hold of unchristian Solomon’s wisdom yet. But he who dodges hospitals and jails, and walks fast crossing grave-yards, and would rather talk of operas than hell; calls Cowper, Young, Pascal, Rousseau, poor devils all of sick men; and throughout a care-free lifetime swears by Rabelais as passing wise, and therefore jolly;—not that man is fitted to sit down on tombstones, and break the green damp mould with unfathomably wondrous Solomon. But even Solomon, he says, “the man that wandereth out of the way of understanding shall remain” (i.e. even while living) “in the congregation of the dead”. Give not thyself up, then, to fire, lest it invert thee, deaden thee; as for the time it did me. There is a wisdom that is woe; but there is a woe that is madness. And there is a Catskill eagle in some souls that can alike dive down into the blackest gorges, and soar out of them again and become invisible in the sunny spaces. And even if he for ever flies within the gorge, that gorge is in the mountains; so that even in his lowest swoop the mountain eagle is still higher than other birds upon the plain, even though they soar.`;
const data = text.split(" ").map(text => ({
text: `${text}&nbsp;`
const sel ="article")
.html(d => d.text)
.each(function(d) {
d.width = this.offsetWidth;
d.x = this.offsetLeft;
d.y = this.offsetTop;
.style("position", "absolute")
.style("white-space", "nowrap");
const lineHeight = data.filter(d => d.y)[0]?.y || 18;
const cumsum = [0, ...d3.cumsum(data, d => d.width).slice(0, -1)];
for (let [i, d] of Object.entries(data)) {
d.i = i;
d.cumx = cumsum[i];
function render() {
for (let d of data) {
d.x = d.cumx % innerWidth;
d.y = ~~(d.cumx / innerWidth) * lineHeight;
.style("left", d => `${d.x}px`)
.style("top", d => `${d.y}px`);
const ghostData = data.filter(d => d.x + d.width > innerWidth);
const ghost ="aside").selectAll("span")
.data(ghostData, d => d.i)
.style("position", "absolute")
// .style("color", "gray")
.html(d => d.text)
.style("left", d => `${(d.x + d.width) % innerWidth - d.width}px`)
.style("top", d => `${d.y + lineHeight}px`);
window.addEventListener("resize", render)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment