/cdn.vox-cdn.com/uploads/chorus_image/image/53816029/th_lede.0.png)
Last month the Storytelling Studio, in collaboration with Vox.com editorial and video teams, took our audience on a treasure hunt. This is the story of the annotated poem, which is central to the narrative.
As soon as we sat down with Vox.com reporters Zachary Crockett and Estelle Caswell, it became clear that Forrest Fenn’s poem played a critical role in not just their experience, but in the theories and journeys of all treasure seekers that Fenn has captivated since burying the treasure in 2010.
Fenn’s poem is the map to his treasure. It isn’t a typical treasure map with marked paths or an “x marks the spot,” but a 24-line poem containing nine clues to where the treasure is hidden. Any theory worth investigating is based on clues mentioned in this poem.
While discussions about design, layout, and user interaction wouldn’t happen for another few weeks, we came out of that first meeting with a basic understanding of the story we wanted to bring to life. And one thing I recognized immediately was that this poem would be mentioned early and referenced often.
Start with inspiration
When starting any project, we typically complete some kind of competitive analysis. So I did a bit of looking around to see how others have handled annotations on the web. I started with Vox’s annotated version of Trump’s Inauguration speech, which some of my teammates worked on with the Vox.com editorial staff, and “Donald Trump’s Inaugural Speech, Annotated,” by the New York Times.


While Vox.com places the annotations under the related paragraph at all breakpoints, I liked how NYT piece floated the annotations to the right of the speech content on wider breakpoints, only moving them underneath when necessary. While both versions adequately accomplish the task at hand, the NYT version resonated with me because the style reminded me of annotations that I would write. Notes scribbled in the margins of articles or books — this feels real to me. This is how I work, and the way that I’d annotate the poem if I were going on this journey.
Experiment and iterate
I was lucky enough to be given creative freedom to figure out how to best display these poem annotations, so I tried a few things.
I first tried floating the annotations to the right of the poem and horizontally aligning them with the matching clue (as closely as possible).
:no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/8176303/Screenshot_2017_01_31_10.49.49.png)
After writing a bit of collision detection for the annotations to prevent them from overlapping, I found that this approach technically “worked” but looked rather messy and didn’t give off the same polished feel that the rest of the assets in the piece do.
My next thought was to space the annotations out evenly in the right column. They’d no longer be aligned with the corresponding clues, but they’d get a bit more breathing room.
:no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/8176393/Screenshot_2017_03_17_13.52.02.png)
This was a good start, but in giving the annotations this much-needed space, the element of proximity that had been tying the annotations to the clues had now been erased. I needed to reestablish the link between the annotations and the clues. This is where I drew most heavily on inspiration from the NYT piece, both in visual appearance and in technical execution.
Technical implementation, a step-by-step overview
While I can’t speak to the exact technical implementation of the annotations in the NYT piece, I do know a few things about how it works. It first creates a Scalable Vector Graphic (SVG) set to the same width and height as the content it’s annotating — essentially creating a blank canvas for the annotations to be drawn on — and sets it behind this content. Then, using SVG Path elements, draws lines connecting the highlighted sections to the annotations.
:no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/8176631/Screenshot_2017_03_17_14.10.10.png)
I also created an SVG that scales to be the same height and width as the content (the poem), but instead placed it on top (rather than under) the content. For the sake of allowing the user to highlight the text more easily, I’d also place the SVG behind the content if I were to ever create something like this again.
Adding the lines
After creating the SVG, sizing it, and aligning it with the top of the poem, I needed to create the starting and ending points or each line.
The starting points (I used svg circles) for each line are all based on the relative position of each of the clues within the poem container. Specifically, the cx
(position from left) and cy
(position from top) are based on the relative top and bottom positions of each clue.
- The position from the left,
cx
, is created by adding the clue’s position from the left + the width of the clue itself + 15px. - The position from the top,
cy
, is created by taking the clue’s position from the top and subtracting 13px (so that the point is vertically centered with the clue).

The code below demonstrates how to create the point shown above corresponding to the first clue.
var pos = $("#clue0").position();
d3.select('#poem-lines').append('circle')
.attr('id', 'c'+i)
.attr('cx', pos.left + $(this).width() + 15)
.attr('cy', pos.top - 13)
.attr('r', 3)
.attr('fill', 'red');
After creating points to the right of each clue and to the left of each annotation, it was time to connect them with the svg path
element. For a more detailed explanation on creating svg paths, I'd highly recommend checking out this great introduction by Dashing D3.js.
Here's the code used to created the initial lines between the clue points and the annotation points:
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
# Select each of the poem annotations
$.each($('#poem-annotations div'), function(i){
# Select the x and y positions of the corresponding clue and annotation points
var lineData = [
{ "x": d3.select('#c'+i).attr('cx'), "y": d3.select('#c'+i).attr('cy')},
{ "x": d3.select('#a'+i).attr('cx'), "y": d3.select('#a'+i).attr('cy')}
];
# Append the corresponding path to the svg and call
# the line function that will draw the line
d3.select('#poem-lines').append("path")
.attr("d", lineFunction(lineData))
.attr("stroke-width", 2)
.attr("stroke", "blue")
.attr("fill", "none");
});
The result looked like this:
:no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/8177327/Screenshot_2017_01_31_13.31.34.png)
Styling the lines
The points were connected now, but the lines were very sharp and didn’t have the softer, hand-drawn feel to them that I had envisioned.
While I’m sure there are multiple ways to do this, I chose to add extra points to the lines themselves, one 20px to the right of the leftmost point and one 20px to the left of the point on the farthest right to create the additional sections I needed.
Updated lineData
containing the two new points as the two middle items in the array:
# The two middle items, lineData[1] and lineData[2] are
# the two points that we are adding to the line
var lineData = [
{ "x": d3.select('#c'+i).attr('cx'), "y": d3.select('#c'+i).attr('cy')},
{ "x": parseInt(d3.select('#c'+i).attr('cx')) + 20, "y": d3.select('#c'+i).attr('cy')},
{ "x": parseInt(d3.select('#a'+i).attr('cx')) - 20, "y": d3.select('#a'+i).attr('cy')},
{ "x": d3.select('#a'+i).attr('cx'), "y": d3.select('#a'+i).attr('cy')}
];
The code above produces lines looking like this:
:no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/8189845/Screenshot_2017_03_20_11.01.35.png)
Though this doesn’t create the “swoopy” lines you see in the end result, it was a good starting point. Then, by adding .interpolate("basis")
to the lineFunction
I’m calling, I was able to smooth the lines out to achieve the effect that I was looking for.
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("basis");
:no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/8189913/Screenshot_2017_03_20_11.01.10.png)
Adding the arrows
The lines were looking better now, but they still felt a bit unfinished. I toyed with the idea of using circles on both ends of the line, which probably would have looked alright, but decided to give the arrows a shot since they most closely imitate the annotations style that I’d draw by hand.
Adding the arrows was pretty simple since they’re actually just lines created using svg paths (like the lines drawn between the annotations and the clues). It uses the same lineFunction
shown above with an updated set of lineData
based on the clue position and width (similar to how we added the points before).
All of the arrows are facing left, so the first item in lineData
is above and to the right of (the now invisible) point we added earlier. The middle item in the lineData
array is placed at the same height as this point and slightly to its left, while the third item is to the right of and below the point.
$.each($('.clue'), function(i){
var pos = $(this).position();
# Line data used to create the leftward facing arrows
# pointing to each highlighted clue
var lineData = [
{ "x": pos.left + $(this).width() + 23, "y": pos.top - 18},
{ "x": pos.left + $(this).width() + 13, "y": pos.top - 13},
{ "x": pos.left + $(this).width() + 23, "y": pos.top - 8}
];
d3.select('#poem-lines').append("path")
.attr("d", lineFunction(lineData))
.attr("stroke-width", 2)
.attr("fill", "none");
});
The end result becoming this:
:no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/8190237/Screenshot_2017_03_20_11.57.48.png)
By scaling and redrawing the annotations on resize, this design works at various breakpoints. It eventually switches to a different view around 600px wide.
Swapping to footnotes
While I initially tried putting the annotations under each stanza, similar to how the Vox.com annotations format adds annotations underneath each corresponding paragraph, this broke the poem up too much and created a rather disjointed reading experience. Instead, I opted to put them all below using footnotes to connect the clues to the annotations.
In the on-platform version, the clues and annotations are tappable on smaller screens. This was actually a suggestion that came out of one of our user testing sessions. Since the poem and annotations no longer fit within one frame, the user felt that it would be helpful to be able to navigate quickly between them. Adding this feature was our response to that suggestion.

Tying the poem into the narrative
Given the critical role the poem plays in Zack and Estelle’s journey, it was important for us to clearly show how directly tied many of Zack and Estelle’s actions were to the clues in the poem.
Our solution was to borrow from the design language we established in the poem and use the same style to highlight these clues interspersed throughout the narrative. This immediately makes clear that these clues were mentioned earlier and prevents the user from having to guess or scroll back up to the poem for reference.
:no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/8197193/Screenshot_2017_03_21_12.21.21.png)