How We Made Youth Radio’s West Side Stories

These youth-made maps show Oakland’s gentrification from another perspective

West Side Stories maps the gentrifying landscape of Oakland through young people’s eyes.

Gentrification has long been a subject of keen interest for us at Youth Radio, the Peabody Award–winning, youth-­driven production company that made West Side Stories.

In our region, San Francisco has gotten most of the attention on this issue, with protests against tech companies’ shuttle busses and skyrocketing housing costs making national headlines. Recently, Oakland is emerging as our latest contested city. More and more longtime residents are moving out, and tech workers and their employers are moving in. Case in point: Uber just announced plans to take up residence in what was once a huge Sears department store in the heart of Oakland’s downtown.

Youth Radio is based in downtown Oakland too. Every day, we watch the blocks around us change. But in nearby West Oakland, where we focus our new interactive, the transformation is even more striking.

Introducing: West Side Stories

Whether in the Bay Area or elsewhere across the country, stories about gentrification tend to reduce the dynamics to one narrative. Newcomers displace longtime residents, erasing history, shifting the economy, and disrupting culture in the process. Focusing on West Oakland, the Youth Radio team behind West Side Stories wanted to surface the many nuanced and sometimes conflicting stories sparked by extreme neighborhood change. And we wanted to share those stories through young people’s eyes. So we decided to create a map, and to fill it with original videos, audio interview clips, and illustrated icons showing what West Oakland has been, and the place it’s becoming.

Map of Oakland image

Mapping the new Oakland.

Collaborating with a small team of professional designers and developers, the young people who worked on West Side Stories recorded a variety of voices, including longtime residents and more affluent couples shopping at a local open house. They highlighted little­-known facts, hidden beauties, and traces of struggle. Also, they felt that newcomers making their homes in this place, which played such an important role in the civil rights movement, should know something about West Oakland’s history. So on the map, you’ll find glimpses into the recent and not­-so-­recent past.

Image from app, sketch of building

Marking familiar places, across generations.

About Youth Radio and YRI

West Side Stories is a project of the Interactive division of Youth Radio, a non­-profit production company founded in 1992. More than 300 young people annually learn hands-­on, tech­-enabled media production through free after­-school classes at Youth Radio (we connect with thousands more through outreach). Our students are primarily youth of color and those growing up in low-­income communities. After completing six months of coursework, they apply for paid positions across the organization, in a range of roles including peer educators, event organizers, community engagement supporters, reporters, designers and developers.

One position young people can apply for is with the Interactive department, which we call YRI, established five years ago. Interns in YRI collaborate with adult designers and developers ­­both inside Youth Radio and with our partners at MIT and UC Berkeley ­­to create mobile apps and online interactive news content. YRI works closely with Youth Radio’s newsroom, which serves as NPR’s youth desk and is known for its award-­winning features, commentaries, and investigative series by youth about youth, which appear not only on NPR and local public radio affiliates, but also on outlets including National Geographic, Medium, The Huffington Post, and Boing Boing.

Seven young people between the ages of 16 to 20 were lead contributors to West Side Stories, dividing among them the work of framing the concept, producing and pitching wireframes and detailed designs, identifying and carrying out interviews, gathering and fact­-checking data, creating graphic assets, contributing to the code, testing, and helping to share and represent the project in public forums and the press.

The youth team

The youth team.

They did all this work in close collaboration with an adult team that included a lead developer, designer, editor, and producer­-educators. In all, it took about three months to build and populate the prototype, and a few more months to polish and ultimately publish.

Getting Started with West Side Stories

For this particular project, the creative process started with a joint brainstorm between youth teams working in YRI and the newsroom. Youth reporters were developing some radio stories on the topic as part of KQED’s Boomtown series, ​so this was our opportunity to create an interactive tied to that reporting.

We’d tossed around lots and lots of ideas. A heat map showing where gentrification is most intense. An interactive guide showing how not to be a jerk when you move to a new place. A slider that allows you to visualize change across decades. A tool to track the concentration of the lesser­-known cultural symbols young people see as indicators that gentrification is taking hold. The youth team talked through these ideas and more, did a lot of research (including a visit to the archives at the Oakland library), and finally agreed on the core concept that became West Side Stories: a media-rich map.

Images of creative process

Early brainstorms and research.

Why a Media-Rich Map?

Over the years, Youth Radio’s newsroom had produced plenty of original videos and radio pieces based in West Oakland that we could feature. With a media-rich map, we had an advantage—we wouldn’t really be starting from scratch.

Images from the app

Pieces of a media-rich map.

We had a second advantage as well: access to an example that provided design inspiration and an open source code base to build on. Youth Radio’s lead developer shared Young Hahn’s Sherlock Holmes Map with the youth team, and encouraged them to think about ways they could take inspiration from the basic functionality of that project and make it work for West Side Stories.

The Code

At YRI, we try our best to involve our young staff in every aspect of the app development process. However, coding something from scratch isn’t easy and takes time, even for veteran developers. That’s where t​his​ article came in handy. In this tutorial, the author shares the starter code that helped us build West Side Stories. It was also inspiring because it involved both a map and storytelling in a way that was visually appealing and user­friendly.

While we had to make some modifications (the code is deprecated) and update some of the mapbox.js methods, the skeleton was there, which made it easy to teach the basics of map creation with mapbox.

For example, we wrote some code and left some instructions so that our interns could fill in the blanks, introducing the concept of coordinates and objects in JavaScript.

        L.mapbox.accessToken = [FIND TOKEN AND PLACE IT HERE];
        window.map = L.mapbox.map('map', MAP ID GOES HERE, {
            zoomControl: false
        }).setView([COORDINATES], 15);

While the base code was heavily edited, we kept some key functions intact. For instance, we wanted to use custom markers in order to add visual context to the location it pointed to.

var defineCssIcon = function(spots) {
    spots.eachLayer(function(e) {
        var className = 'map-icon icon-' + e.feature.properties.id;
        var coordinates = e.feature.geometry.coordinates;
        var html = "<div data-id='" + e.feature.properties.id + "'></div>";
        var cssIcon = L.divIcon({
            // Specify a class name we can refer to in CSS.
            className: className,
            // Set marker width and height
            iconSize: [80, 80],
            html: html
        if (e.feature.properties.id === "about") {
            L.marker(coordinates, {
                icon: cssIcon
            }).addTo(map).bindPopup('<h1> Youth Radio Interactive HQ </h1>');
        } else {
            L.marker(coordinates, {
                icon: cssIcon


// Set background image for icon
var defineBgIcon = function(e) {
    if (e.feature.properties.bg_icon && e.feature.properties.bg_icon_active) {
        var className = '.icon-' + e.feature.properties.id;
        var bg_img = 'url("https:' + e.feature.properties.bg_icon + '")';
        var bg_img_active = 'url("https:' + e.feature.properties.bg_icon_active + '")!important';
        var icon_active = className + '.active';
        // set css classes based on info from the data we received.
        $(className).css('background-image', bg_img);
        jss.set(icon_active, {
            'background-image': bg_img_active

        if (e.feature.properties.id === 'about') {
            var bg_img = 'url("' + e.feature.properties.bg_icon + '")';
            var bg_img_active = 'url("' + e.feature.properties.bg_icon_active + '")!important';
            $(className).css('background-image', bg_img);
            jss.set(icon_active, {
                'background-image': bg_img_active

Here, we created specific class names that we could refer to in our css and define a specific background image for each icon. We also added some custom HTML to each marker with a custom ID in order to create the interaction between the sidebar and the map.

narrative.onscroll = function() {

    var narrativeHeight = narrative.offsetHeight;
    var newId = currentId;
    // Find the section that's currently scrolled-to.
    // We iterate backwards here so that we find the topmost one.
    for (var i = sections.length - 1; i >= 0; i--) {
        var rect = sections[i].getBoundingClientRect();
        if (rect.top >= 0 && rect.top / 0.5 <= narrativeHeight) {
            newId = sections[i].id;

    function setId(newId) {
        // If ID hasn't change, do nothing
        if (newId === currentId) return;
        if (newId === 'cover') {
            $('body').attr('class', 'section-0');
        } else {
            $('body').attr('class', ' ');
        // otherwise, iterate through layers, setting the current marker to a different style and zooming to it
        spots.eachLayer(function(layer) {
            if (layer.feature.properties.id === newId) {
                var coordinates = layer.feature.geometry.coordinates;
                var all_el = $(".map-icon");
                var el = $("div[class*='" + newId + "']")[0];
                el.className = el.className + " active";
                if (newId === "cover") {
                    map.setView(coordinates, 15);
                } else {
                    map.setView(coordinates, 16);
        // highlight the current section
        for (var i = 0; i < sections.length; i++) {
            sections[i].className = sections[i].id === newId ? 'active' : '';
        // And then set the new id as the current one,
        // so that we know to do nothing at the beginning
        // of this function if it hasn't changed between calls
        currentId = newId;

Note that we also used a library called jss.js, which is a JavaScript library for getting and setting CSS stylesheet rules. This library was added in the latest version of the interactive in order to make it dynamic and allow our newsroom to add stories to the map without writing any code.

The first version of the interactive was basically hard­coded. To be honest, we didn’t think about the future as much as we should have. Once we did get around to it, we quickly realized that we needed a dynamic version so that the newsroom could update the map without a developer.

To make that happen, we used keystone.js as an admin interface for the backend and built an API exposing all the posts data, and we refactored the code on the front­end to dynamically load new content onto the map. We wanted to give as much control to the newsroom as possible, something that’s a challenge with dynamic content. Keystone.js was ideal because it really allowed for extended custom data fields and even had an option to sort the posts. That enabled us to sort the posts on the front ­end to match the desired order.

var keystone = require('keystone');
var Types = keystone.Field.Types;

 * Post Model
 * =============

var Post = new keystone.List('Post', {
  autokey: { path: 'slug', from: 'title', unique: true },
  map: { name: 'title' },
  sortable: true

  {heading: 'Info'},
  {title: {type: String, required: true, initial: true},
  css_id: {type: String, label: "Unique CSS ID", required: true, note: "Make sure the id is unique", initial: true},
  bg_color: {type: Types.Color, required: true, initial: true, note: "Input rgba and make sure the transparency is set to .8"},
  state: { type: Types.Select, options: 'draft, published', default: 'draft', note: "Draft will go to youthradio.org/yristaging - Published will go to youthradio.org/westsidestories" },
  source: {type: String},
  location: {type: Types.Location, required: true, initial: true},
  description: {type: Types.Html, wysiwyg: true, required: true, initial: true}},
  {heading: 'Media'},
  {icon_active: {type: Types.S3File, note: "image should be 120px x 120px and be a .png", filename: function(item, filename){
    return "active_icon" + '_'  + item._id + '_' + filename;
  icon: {type: Types.S3File,note: "image should be 80px x 80px be a .png", filename: function(item, filename){
    return "inactive_icon" + '_' + item._id + '_' + filename;
  illustration: {type: Types.S3File, filename: function(item, filename){
    return "illustration" + '_' +item._id + '_' + filename;
  audio: {type: Types.S3File, filename: function(item, filename){
    return "audio" + '_' + item._id + '_' + filename;
  video: {type: Types.Html, height: 20}}

 * Registration

Post.defaultColumns = "title, location, state";

West Side Stories was one of those projects that brought out the best of YRI’s team. It also taught us some valuable coding and design lessons. Building for re­use and for user independence is simply more sustainable than hard­coded content. Lesson learned.

The Design

Once we had decided on the map and sidebar format, we knew we would need to create icons or photos to represent the location of each piece of content. At first, we considered doing classic icons based on media type—audio, video, or text (see sketches below). But when we looked at the media icons on the map, it became clear that we needed visuals to give users a stronger sense of place and story. So it was back to the drawing board­—literally­—to come up with individual icons for each piece of media.

Icons in two stages

Preliminary sketches (left) and later stage prototypes of media icons (right). While we didn’t end up using these icons, you can see the round shape and soft color palette start to emerge in these early drafts

During our brainstorm, we returned to the framing of the interactive and decided we wanted the visuals to resonate with people who lived in the neighborhood. We pulled up online images of each piece of content’s location using Google maps, and then asked Youth Radio students who lived in the neighborhood if there were any characteristic landmarks or symbols associated with that place.

For example, one of the audio interviews (Malik) occurred at an intersection with a well­known local mural. Another (Kevin) was located near a really big tree that residents use as a landmark. To account for the fact that many of the interviews took place geographically close together (and we didn’t want the icons to look too much alike), we also included narrative symbols like “for sale” signs to represent the content.

Icons from the app

Malik’s icon (left), based at 14th and Campbell in West Oakland, showcased a local mural. Kevin’s icon (right), located in the Lower Bottoms neighborhood, includes a large tree used as by residents as a landmark.

To standardize the look/feel of the visuals, we decided to hand­-draw each icon as an illustration using Photoshop CS6 and a Bamboo sketch pad and stylus. Too often, “urban” environments are depicted using cliche images (brick wall, chain link), fonts (graffiti­style), and colors (dark, grays, caution­tape yellows). We adapted our source images using thick, free-­form lines and a soft but saturated color palette to give the interactive a handmade, positive, and welcoming look/feel.


Top: 16th Street Station base image layer (via Creative Commons) and color palette options. Second row, left: ​16th Street Station base image layer with brush tool sketch layer added (left). With orange color from palette added beneath sketch layer (right). Bottom row: 16th Street Station illustration with base image removed (left) and large map icon (right).

Next Steps + Lessons Learned

West Side Stories is dynamic and will grow. With help from The Atlantic’s CityLab, Vox and now Source, we’re spreading the word about this project now, inviting our community to submit additions to the map, and to stay connected as we roll it out across the Bay Area and nationally.

Call to submit

Submissions from the community accepted.

But of course, with the great news that people are actually using the map comes a challenge: to carve out space for a second­-wave editorial process that includes communities outside Youth Radio as co­-producers. Right now we are in the process of figuring out how we’ll continue to update the map moving forward. Which new submissions do we include? Should we solicit stories we know we want? What about the requests we’ve received for versions of map in new neighborhoods­­, whether in nearby East Oakland or far-­flung spots around the country, where residents want to see the specifics of their own experiences on display?

This is, of course, precisely the kind of challenge we seek. As evident in our name, Youth Radio Interactive, a goal for all of our projects is to spark community engagement of some kind, and West Side Stories is no exception. How do we make the most of the interest this project has triggered, and how do we make good on its potential to serve as a dynamic platform for community storytelling?

We will approach this set of questions in the same way we do any production challenge­­—by gathering a cross­-generational team, examining various options, connecting with community partners, and assessing the range (and limits) of resources we can put to this project. Out of that, we’ll establish a strategy and workflow for updates, which we expect to include initial Bay Area expansion in February, and a national push by Spring 2016.

Youth are Not Just Subjects

In reflecting on how we made West Side Stories, one thing jumps out with particular relevance to Source readership. Sure, Youth Radio is uniquely set up to include young people in every aspect of producing news interactives. Our young people are supported by a full­-fledged media education program and learn key skills that enable them to hit the ground running when we start a new project. But building interactive stories, apps, and infographics in any newsroom involves such a diverse array of tasks—­­conceptualizing the big idea, identifying and talking to sources, collecting and analyzing other forms of data, helping to design the user experience, fact­-checking, and engaging communities around the project.

Young people absolutely have the capacity to work across this range of roles, and it makes such a huge difference for them to do so, especially on stories in which they have a lot at stake. Youth Radio is built on the proposition that young people are creators of knowledge, not just receivers; that they are story-­makers, not just subjects; that they are technology developers, not just users; and that they are researchers, not just the researched. We love that Source highlights “journalism code and the people who make it.” And we want more and more young people who wouldn’t otherwise be a part of that amazing process to use every tool at their disposal to make themselves seen, heard, and understood.




Current page