Code, Design, and Growth at SeatGeek

Jobs at SeatGeek

We are growing fast, and have lots of open positions!

Explore Career Opportunities at SeatGeek

Introducing Sixpack-java: A/B Testing for Android and Java Apps

A Java client for the Sixpack A/B testing framework

The SeatGeek engineering team is excited to announce the latest addition to the Sixpack A/B testing framework’s client list: sixpack-java. Designed with the goal of making A/B testing Android applications easy and painless, sixpack-java has a straightforward API and an easy setup process that should make measuring and analyzing your application design decisions a breeze.

If you’re unfamiliar with Sixpack or A/B testing in general, you can read more about it here.

Let’s take a look at how you might integrate sixpack-java into your Android app.

Android app integration

Note: it is assumed that before integrating the java client into your app that you have set up a running instance of Sixpack-server; for information on setting one up, check out the instructions here

First, you’ll need to add sixpack-java to your application dependencies. sixpack-java is available on the Sonatype snapshots repository while it’s in beta, so you’ll need to add the following to your build.gradle:

1
2
3
4
5
6
7
repositories {
    maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}

dependencies {
    compile 'com.seatgeek:sixpack:0.1-SNAPSHOT'
}

Now that you’ve resolved your dependencies, you can add an A/B test to your app. Let’s initialize a Sixpack client by creating a new Sixpack instance using the SixpackBuilder:

1
2
3
4
Sixpack sixpack = new SixpackBuilder()
        .setSixpackUrl("http://api.mycompany.com/sixpack")
        .setClientId(getCachedClientId())
        .build();

For reference, your getCachedClientId() method might look something like this:

1
2
3
4
5
6
7
8
9
public String getCachedClientId() {
    SharedPreferences prefs = context.getSharedPreferences("sixpack", Context.MODE_PRIVATE);
    String clientId = prefs.getString("sixpack_client_id");
    if (clientId == null) {
        clientId = Sixpack.generateRandomClientId();
        prefs.edit().put("sixpack_client_id", clientId).apply();
    }
    return clientId;
}

There are a two important things to take note of here: - You need to replace the url used in the setSixpackUrl() call with the url that points to your Sixpack-server deployment - The client id. The client id is the identifier used by the Sixpack-server to keep track of which clients have received which alternatives in the experiments they’re participating in. It is very important that the client id doesn’t change between sessions or else your users may see one alternative one time visiting the app and then another the next. That will probably have a fairly significant impact on your results too, so, just don’t do it. We recommend generating the client id once (there’s a helper method available, Sixpack.generateRandomClientId()) and caching that value in SharedPreferences so that it can be used again later (see the above code for an example of how that might work).

Note: we recommend maintaining a singleton instance of Sixpack using your favorite DI implementation, we use dagger for this

Alright, now you can create an experiment for testing the color of a button in your UI:

1
2
3
4
5
Experiment buttonColor = sixpack.experiment()
        .withName("Button Color")
        .withAlternative(new Alternative("Red"))
        .withAlternative(new Alternative("Green"))
        .build();

This will create a new experiment called “Button Color” with two alternatives, “Red” and “Green”. Once your client starts participating in this experiment, you’ll see it show up in the Sixpack-web dashboard. So let’s start it!

You start the test by calling Experiment#participate() and passing in the appropriate callbacks. Java 8 lambdas are used here for brevity.

1
2
3
4
5
6
7
8
9
10
buttonColor.participate(
        (participatingExperiment) -> {
            // success! save the participating instance for later so that we can convert it and set our button color
            this.participatingExperiment = participatingExperiment;
            button.setBackgroundColor(participatingExperiment.selectedAlternative == redAlternative ? R.color.button_red : R.color.button_green);
        },
        (experiment, error) -> {
            // failure, check network connection and try to participate again, you should also likely fallback to a default
        }
);

Now your test is live in the dashboard!

Finally, when the user clicks the button (assuming that’s the action that you’re measuring in this example) you can go ahead and fire the convert() message to Sixpack.

1
2
3
4
5
6
7
8
9
10
public void onClick(View button) {
   participatingExperiment.convert(
            (convertedExperiment) -> {
                // success!
            },
            (experiment, error) -> {
                // failure, check network connection and try to convert again
            }
    );
}

And that’s it, you’ve now successfully tested that button color!

Advanced usage

You might take a look at this API and say that there are several calls that do not need to be repeated more than once, and you’re not wrong! For instance, if you have a heavily trafficked part of your app under test, it will be to your advantage to call participate early in your application’s lifecycle and cache the ParticipatingExperiment in a way that will make fetching the selected alternative and calling convert() as easy as possible as to not hold up the rest of your UI from initializing. That said, participating early is risky and isn’t appropriate for most cases because you don’t want to participate in a test that your user never gets a chance to convert. Be careful and be sure to validate your tests are calling participate and convert at the right times before going into production by using a proxy or logging.

Here are a few “advanced usage” scenarios that you may want to consider for improving the sixpack-java integration in your app:

  1. You could expose your ParticipatingExperiments as RxJava Observables and .cache() the results
  2. In addition to having a singleton Sixpack instance in DI, you can put your Experiments and ParticipatingExperiments in your dagger modules so that they can have a lifecycle outside some of your application’s components
  3. Instead of generating a random client id, for your users that are registered and have ids within your own system, you can use their user uuid as the Sixpack client id and later use Sixpack-server’s API to join your A/B testing results with your application’s users!

Conclusion

We can’t wait to see the great things you’ll build with sixpack-java! We’ve been using sixpack-java internally, but it is beta software at the moment, so please integrate it into your apps and let us know if you have any issues with it here at our github.

Have fun building!

P.S. If you’re interested in helping us create the best mobile event ticketing experience on Android, we’re hiring.

SeatGeek 3.0 for Android

Today marks the initial release of SeatGeek 3.0 for Android. Much has changed since 2.0 was released last fall, all in the spirit of making it easier for you to do everything you love to do with SeatGeek. You can track your favorite teams and artists, see high-res views from every section of a venue, and — of course — buy tickets.

Let’s take a look at what’s changed since our last major release.

Save your payment and shipping information, and buy tickets in seconds. New in today’s release is a feature that eliminates everyone’s least favorite part of buying something on your phone — having to enter your billing and shipping information … Every. Single. Time. With SeatGeek 3.0 for Android, you can store your credit cards and delivery addresses. When you find the perfect tickets, just select a saved card and address, click “Buy Now”, and presto, you’re done! Ship tickets to your home, office, or a friend, and never worry about entering the same information twice.

Import your favorite teams and artists. We’ve added the ability for you to connect your Facebook account and favorite music services to your SeatGeek account. Once you’ve connected, SeatGeek will provide you personal event recommendations based on the teams and music you love, and you’ll receive notifications about games and shows near you.

Find and edit important account information from the redesigned Settings view. As part of a broader material design integration, we’ve updated Settings in today’s release to make navigating the details that power your SeatGeek experience simpler and more intuitive. You can come here to edit your location or add favorite performers from connected services for new event recommendations. All your payment information is accessible here as well in case you need to add, delete or update a credit card or delivery address.

Get money back with promo codes. Maybe you’ve heard a SeatGeek ad on the radio, received a card in the mail, or seen us on the subway. Notice the promo code in those? Now you can enter it in the app and have that offer instantly added to your account. Next time you grab tickets for an event, your rebate is automatically triggered. Same tickets, even better deals. Nice!

See the view from your seat — before you get there. This fresh design lets you feel what it will be like to be at your event with full-width view-from-seat images. Without even having to scroll, you’ll also see key information about the event, seat location, seller, and ticket price. You can change the number of tickets you’d like and choose from your saved payment and delivery addresses, or stick to the defaults and buy immediately from this screen.

The updated Android app is rolling out to users this week in the Play Store, so check it out when you get the update, and let us know what you think!

Stretching a Dollar at the Home Run Derby

Most people go to the Home Run Derby to see one thing: star players hitting long bombs. They probably hope to come away with a souvenir, too, which is why some of the priciest tickets for the derby every year are those in the outfield.

Knowing that fans buy tickets in home run territory at a premium, we set out to build a tool that shows which sections at this year’s derby give fans the best chance of catching a baseball — and based on the going rate for tickets in those sections, which represent the best value for home run hunters.

If you’d prefer to cut to the chase and see the results, you can check out our finished Guide to the 2015 Home Run Derby tool here.

Method

Once the participants in this year’s derby were announced, the first step in our process was to use ESPN’s Home Run Tracker (major h/t!) to map their home runs to Great American Ball Park.

Next, we went about predicting the number of home runs we expected to be hit in this year’s competition. We accounted for several factors, including the new bracket-style format, the average amount of home runs hit at the derby historically, and where this year’s hitters’ have hit home runs in 2015. In the end, we came to an expected homer count of 89, 67 of which we expected to be catchable (meaning that they would land in a seating section, rather than the non-spectator areas in center field).

We then distributed those predicted home runs by section in proportion to the distribution of actual home runs that have been hit by the eight participants in MLB games this year. This gave us an expected number of home runs per section, and we divided the average resale price for each section by its expected home runs to come up with a relative value (“HR cost” in the table).

Interesting Findings

This year’s class of hitters distribute their home runs relatively fairly, with right field and left field seeing a similar number of home runs. Manny Machado and Josh Donaldson have hit homers to both corners of the park this year, while Kris Bryant hits most of his home runs to center and left-center field.

Overall, the section with the best value is Section 105, located down the left field line. We expect around 12 home runs to land in this section during the derby, which partially explains why it has the fourth-highest ticket prices among sections in the outfield.

With no access to dead center field, it seems as though many home run balls will end up uncatchable.

While the corners are certainly the top spots to expect home runs, interestingly it is the right-center Section 144 and left-center Section 102 where we expect the second- and third-most home runs to be hit. Fans in Section 102 should pay particular attention when Todd Frazier is at the plate, while those in Section 144 should watch out when Joc Pederson is swinging.


The only sections where we highly doubt a home run ball will reach are Sections 402 and 403, located in the second level of center field. None of this year’s participants have hit a home run this year that would land in this area of the park. The fans in those sections should pay particular attention when Albert Pujols is at the plate, though, as this year he has shown the best ability to get the ball to fly that far to left.

Potential Pitfalls

Predicting the amount of home runs hit at the derby, and where they will land, is certainly an inexact science.

Below are just some of the potential problems:

  • We didn’t take into account each batter’s odds to advance, which would have impacted the distribution of predicted home runs.

  • We didn’t account for weather — the forecast for the derby is not ideal, and that could have a serious impact on how the ball travels.

  • We mapped home runs based on major league pitching, not the batting-practice soft toss batters will actually face Monday night. If we had a reliable data set on where these hitters have launched BP homers, that might have been a useful input.

  • With a new format this year, the historical data on how many home runs have been hit at previous derby competitions was less directly applicable. We had to guesstimate the amount of swings each batter will take in the five-plus-minute at-bats, and then go from there to figure out a total number of expected home runs.

Big shoutout to former SeatGeek intern Josh Rosenfeld, who built the original version of this project last summer.

Animation: Giving Life to Live Events

Visual aesthetic is inherently a part of any live event experience, whether it’s a sporting event, concert or Broadway show. We’ve always focused on trying to capture the rich imagery of sports and music with performer photos, detailed venue mapping and view-from-seat photos, but we’ve recently begun to bring our live event visuals to life as well.

If you’re a SeatGeek user and fan of an NBA or NHL playoff team, chances are you’ve seen one or more of our animations in your inbox. What I’d like to do here is share some insight into my design process and hopefully inspire you to explore the tools and resources out there that you can use to create animations of your own.


Getting started

Before putting pen to tablet, I like to look around and see what’s out there for both inspiration and reference material. When thinking about imagery for our NBA and NHL playoff emails, I turned to sports blogs and news sources for photo references, but my go-to sites are dribbble, niice.co and designspiration.

Sketching

This is the most important step in my design process. It helps eliminate any lame ideas and figure out what will and will not work. Personally, I love using a Wacom tablet and Photoshop; you can duplicate ideas and mess things up (in a good way) very quickly.

Typically, an idea starts out as either mega-rough, rough, or close-but-no-cigar, but if the feeling is right with even a mega-rough idea, you can often skip the latter two stages and move straight into Illustrator.

Artwork

At this point, there is still some decision making going on: Can I remove anything? Can I add anything? “Less but better” is a mantra I try to live by. It’s easy to confuse simplicity with lacking detail and difficult to strike a balance between the two. It’s easy to get caught up fussing over minutiae; I often find myself in the land of endless-possibilities-and-details.

I still watch a lot of tutorials on creating vector art, because there’s always something to new to learn. I highly recommend http://www.pixelcasts.io/ if you’re looking to soak up some valuable Adobe Illustrator knowledge.

Animation

Going into the animation process, what I try to do is reduce the number of layers and groups in Illustrator. Keeping the shoulders, arms, stick and hands all together makes life much easier once you get this into After Effects.

We can create simple layer structures in Illustrator that will then be matched in After Effects after importing.

If you’re interested in learning more, I can’t recommend this Skillshare class on Simple Character Animation enough. It’s where I learned most of what I know about After Effects animation!

Digital Groundskeeping: Redefining ‘Attention to Detail’ in Our Baseball Seating Charts

If you’ve read even a handful of product-related posts on ChairNerd, this isn’t news to you: at SeatGeek, user experience is king. It’s why we’re intensely focused on continually improving the quality of our seating charts, which should be the ultimate source of context in the process of purchasing a ticket.

Recently, we’ve enhanced the accuracy of the playing surfaces within our NFL, NBA, and NHL maps. But with spring comes the perfect opportunity to spruce up our MLB ballparks, which are perhaps the most individualized set of major sports venues in the U.S.

The goals for these enhancements went beyond the aesthetic. We wanted to make our maps as true to reality as possible, which is why we went to the official rulebook to draw the regulation lines and markings of an MLB diamond:


Before & After: Yankee Stadium (NY Yankees)

Before After Satellite image


Before & After: Progressive Field (Cleveland Indians)

Before After Satellite image

But there’s more to groundskeeping than abiding by the rules of 90 feet and 60 feet, 6 inches. Each ballpark’s infield, outfield and on deck areas are unique, and we’ve incorporated those custom designs into the maps of all 30 MLB venues.

Custom infields

The cut of the grass is just one variable when it comes to an infield – even the shape of the dirt varies from ballpark to ballpark. Fenway has its historic fungo circles; Comerica Park is one of a few parks with a dirt path between home plate and the pitcher’s mound; Rogers Centre has the greenest infield in the big leagues, but no grass. Here are a few of my personal favorite infield renderings:

Boston Red Sox
(Fenway Park)
Cleveland Indians
(Progressive Field)
Detroit Tigers
(Comerica Park)
Houston Astros
(Minute Maid Park)
New York Yankees
(Yankee Stadium)
Toronto Blue Jays
(Rogers Centre)

Custom grass designs

Over time, the grass at MLB ballparks has increasingly become an artistic outlet for head groundskeepers:

David R. Mellor, director of grounds for the Boston Red Sox, has in fact written an authoritative textbook on the subject:

Groundskeepers typically alternate designs throughout the season, but the following are frequently used in each of the 30 MLB parks:

Arizona Diamondbacks
(Chase Field)
Atlanta Braves
(Turner Field)
Baltimore Orioles
(Camden Yards)
Boston Red Sox
(Fenway Park)
Chicago Cubs
(Wrigley Field)
Chicago White Sox
(US Cellular Field)
Cincinnati Reds
(Great American Ball Park)
Cleveland Indians
(Progressive Field)
Colorado Rockies
(Coors Field)
Detroit Tigers
(Comerica Park)
Houston Astros
(Minute Maid Park)
Kansas City Royals
(Kauffman Stadium)
Los Angeles Angels
(Angel Stadium)
Los Angeles Dodgers
(Dodger Stadium)
Miami Marlins
(Marlins Park)
Milwaukee Brewers
(Miller Park)
Minnesota Twins
(Target Field)
New York Mets
(Citi Field)
New York Yankees
(Yankee Stadium)
Oakland A’s
(Oakland Coliseum)
Philadelphia Phillies
(Citizens Bank Park)
Pittsburgh Pirates
(PNC Park)
San Diego Padres
(Petco Park)
San Francisco Giants
(AT&T Park)
Seattle Mariners
(Safeco Field)
St. Louis Cardinals
(Busch Stadium)
Tampa Bay Rays
(Tropicana Field)
Texas Rangers
(Globe Life Park)
Toronto Blue Jays
(Rogers Centre)
Washington Nationals
(Nationals Park)


This project is just one small part of our mission to create the best seating charts in the world by redefining the interpretation and visualization of architectural and geographical data. If you have a background in architecture, design, or art and want to help us, perhaps you’re our next Digital Architect!

Bid Automation on the Adwords API

A data-backed adwords campaign bidder

Adwords is great, but its built-in tools don’t always allow you to solve problems unique to your business. I was recently inspired by this post on Search Engine Land to build something similar off the Google Ads API.

There were a few problems on our account that couldn’t be solved by the Brainlabs solution.

  1. Because of the way our checkout process is structured, Adwords doesn’t know about our transactions. Until recently, this hadn’t been a problem; we have an approximation for transactions set up in Google Analytics, and the main KPIs for most of our campaigns are app downloads and email collection (things Adwords is aware of). But I wanted to use real-time transactional information to inform our bidding.
  2. I didn’t want to set up different scripts or bid modifier charts for each of our campaigns. People behave differently when buying Broadway tickets than they do when buying NBA tickets, and I wanted something that would calculate modifiers on the fly for each campaign.
  3. Finally, the major sports teams are spread out across three time zones. The “right time” to bid up a campaign for the Los Angeles Dodgers is different than for the New York Yankees, so I needed something that could handle those differences as well.

A few months back, we had our quarterly Hackathon and I decided to explore the Adwords API, which is pretty easy to sign up for.

There are several Google Ads API client libraries, like PHP, Ruby, Java, etc. I settled on the Python Libary because I hadn’t ever built anything in Python. This will become abundantly clear when you dive into my code.

You can find the script and instructions on how to set it up here. There are three major components of the script.

  1. Pulling transactions or whatever KPI you’re interested in from your database
    • We use Amazon Redshift, so my script uses the Psycopg2 package to connect to and query our database. If you’re running off MySQL, you can follow the instructions here in order to connect to your DB.
    • Make sure that any reporting you do is in the same time zone as your Adwords account. For example, our database records timestamps in UTC, but our Adwords account runs everything on EST.
  2. Working with the Adwords API
    • This file, adwords_api.py, contains every Adwords API-related call, from getting spend reports to modifying keyword bids.
    • This whole operation only runs on campaigns with a specific label. There are instructions here on how to find your label IDs, or how to modify the script so it runs on every campaign on your account.
  3. Modifying bids
    • This file, create_bids.py, actually calculates the bid modifier for each hour based on an ROI or CPA goal defined by you.

What will future versions do?

  • I’d like to write versions that work on the adgroup and keyword level. Currently, the script only calculates modifiers on the campaign level and then attaches them to every keyword in said campaign.
  • Right now, this script doesn’t take day of week into account. While conversion rate from SEM doesn’t change much day to day, I understand that day of week could be meaningful for some businesses. For SeatGeek’s purposes in particular, a future version that integrates time-to-next-event for each team would be a big improvement.

While this is certainly a work in progress, I hope this script can act as a jumping-off point for others to build something that works for their needs.

Meet SeatGeek: Introducing Tiffany Hu

Name: Tiffany Hu

Role: Web Engineer / Data Scientist

Hometown: Diamond Bar, CA

Hypothetical SeatGeek talent show performance: Dance! It’s my main hobby. Also, I never did the whole middle school talent show ‘do a group dance routine to Spice Girls / NSYNC’ thing, so I think that would be hilarious and really fun to choreograph.

Twitter handle: @tfnyhu

SGFL (SeatGeek Foosball League) Ranking: Like… 10th? There are about a dozen who regularly play, so. I do alright when I’m teamed up with Dallas or Ben Clark.

What was your path to SeatGeek?

I graduated from the University of Chicago, where I studied economics and biology. I loved the classes and research I did, and when I finished school I was definitely still itching to explore and learn. I worked at a tech consulting company in Chicago for two years, where I was a part of two teams that focused mainly on different types of text / language analysis and predictive modeling for clients in a range of domains. I was able to pursue independent research projects that gave me leeway to do a lot of programming, and I became hooked. I knew I wanted a role where I could work on both the implementation of data products as well as the exploration and analysis, and I was ecstatic to find that perfect blend at SeatGeek (Steve, head honcho data scientist, dubs it ‘full stack data science’).

What’s the most interesting project you’ve worked on since joining?

Customer coalescence, which is basically looking at all the different combinations of identifiers we might see in actions taken across each platform and collapsing them so they’re associated with various customer “blobs.” Among other things, this gives us a more complete picture of a user’s entire activity history and a more accurate way to attribute user spend. Some of the fun bits were figuring how to best implement this within our ETL pipeline, keeping the processing within Redshift for speed, and using Python to manage the recursive logic that Redshift doesn’t natively support.

What do you want to work on next?

Customer experience is a big focus for SeatGeek right now, and as part of that I’m excited to look more into user behavior, particularly session activity and geolocation. We’re working hard right now to make the user activity data we have more consolidated and accessible for analysis, so that will definitely make it easier for us to tackle those topics soon.

What’s your favorite SeatGeek experience?

Being at SeatGeek has definitely upped the number of events I attend regularly, but I maintain that Slipknot and Korn at Izod Center in December was the best experience I’ve had by far. First off, I’m not a metalhead by any means — in fact, I’d never been to a metal concert before that event. Zack and Rick, two way-more-into-metal SeatGeek devs, made this really enthusiastic effort to get a bunch of the SeatGeek crew to go to this concert, and I was the only one clueless enough to take them up on it.

But it ended up being awesome. I’m not sure what part was best — there were a lot of absurd / great moments. The music I actually ended up loving. The show itself was fantastic (pyrotechnics! midair drumming!). The fans (typically the part that can ruin a show for me) were really nice. At one point, we were kind of this peninsula in a giant mosh pit and a guy next to me asked if I wanted to mosh — I politely declined; it was all very courteous. And then hanging out afterwards, still amped up from the show, was a blast.

What’s your top NYC recommendation — food, fun, neighborhood, etc.?

Xi’an Famous Foods, a fast-casual chain that serves stellar food from western China like cumin lamb ‘burgers’ and spicy hand pulled noodles. I grew up eating Chinese food, so authentic Chinese fast food — especially from a lesser-known region — is a dream come true.

What’s the most interesting thing you’ve read recently?

I came across Dear Data recently. It’s a site where two designers who live across the world from each other put up postcards they send each other every week that contain hand-drawn data visualizations about events they experience in their daily lives. They have a different theme each week, like ‘people’ or ‘time’, and it’s interesting to read why they chose the theme, how they collected their data, and how each of them decided to visualize the data. The results often end up looking nothing alike, so it’s really cool to see how their individual aesthetic styles manifest themselves. And I love that everything is hand-drawn.

What’s been your favorite SeatGeek team event?

Workation last summer, because it started just days after I’d joined SeatGeek and was such an amazing way to get to meet my new coworkers. I definitely lucked out timing-wise! There’s nothing like living under the same roof to get people to bond (and that we were in the Hamptons definitely didn’t hurt).

How We Use Push Notifications to Keep in Touch with Fans

Push notifications are one of the most direct tools we have at our disposal for communicating with our users and keeping them engaged. If we’re smart about how we send pushes, each notification serves as an opportunity to inform users and find out what they care about (and hopefully drive some revenue).

Automated Push Notifications

Automated pushes are really valuable for targeting users based on how long they’ve had the app and their level of engagement since they’ve installed it. Intuitively, the messages that we send to new users should be very different from the messages that we send to people who have had the app for a while and bought tickets before. If we notice patterns in users’ behavior, then we can send them information about events they’re likely to find of interest. With new users, though, we don’t have the accumulated data that’s necessary to know what they like. Instead of accepting the risk that we may be sending them irrelevant information in a push, we target our absence of knowledge.

One of our more successful automated push campaigns is sent to users who have had the app for a few days but haven’t tracked any artists or events. We tested out several different versions of the message, and found that this version performed best:

Chairnerd_Push_1

The copy is informal, but it encourages the user to re-engage with the platform while simultaneously serving as a reminder of our value proposition: SeatGeek can help you find the best deals on tickets. Compared to a control group, this push more than doubled the number of users who tracked events.

One-Off Push Notifications

Sometimes it’s more effective to send one-off push notifications rather than set up a campaign. Major sporting events, like the Super Bowl or NCAA Tournament, occur annually but typically have different participants each year, so it’s not practical to set up recurring campaigns. Instead, we’ll generate a custom list of users we can reasonably assume are interested in the event because of prior purchase or tracking behavior.

For example, if we wanted to send a push notification about tickets for Opening Day at Wrigley Field, we would want to send it to users who have:

  • Purchased a ticket to a Cubs game
  • Clicked out on a ticket to a Cubs game
  • Tracked the Cubs or a Cubs game

We would also be interested in sending the push notification to users who live within a few miles of Wrigley Field, just to be thorough. To pull this list, we would run a query on our internal database that looks similar to the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
SELECT u."id" FROM users u

-- users within a 15 mile radius of Wrigley Field
WHERE acos((sin(radians(41.9483))*sin(radians(u.lat)))
      +(cos(radians(41.9483))*cos(radians(u.lat))
      *cos(radians(u.lon+87.6556))))*3959 <= 15

      -- users with full accounts
      AND u.can_send_email = 1
      AND n.announce = 1

UNION

SELECT u."id"
FROM (

  -- users that have tracked the Cubs
  SELECT DISTINCT up.user_id
  FROM user_preferences up
  LEFT JOIN performers p ON p."id" = up.performer_id
  WHERE p."id" = 11

  UNION

  -- users that have tracked Cubs games
  SELECT DISTINCT n.user_id
  FROM update_notifications n
  LEFT JOIN events e ON e.id = n.event_id
  WHERE e.home_team = 11

  UNION

  -- users that have clicked out on/purchased tickets to Cubs games
  SELECT DISTINCT c.user_id
  FROM clicks c
  LEFT JOIN events e ON e.id = c.event_id
  WHERE c.user_id IS NOT NULL
    AND e.home_team = 11
) AS x

LEFT JOIN users u ON u.id = x.user_id
LEFT JOIN newsletter_signups AS n ON n.user_id = u.id

-- users with full accounts
WHERE u.email IS NOT NULL
    AND u.can_send_email = 1
    AND n.announce = 1
;

These push notifications tend to be much more transactional and are independent of our automated pushes. Generally, we try to communicate three main things with our one-off pushes: excitement through targeting, urgency through copy and timing, and accessibility by showing low ticket prices to the event. The result is a push that looks like this:

One-off_Push

This directs the user to the Cubs Opening Day event page in the SeatGeek app.

While we’re on the topic of pushing things, let me say that the marketing team at SeatGeek is currently hiring! If you’re excited about keeping users happy, active, and engaged, then check out the Marketing Analyst: Retention Specialist position.

Visualizing the NCAA Tournament

March Madness is one of the biggest ticketing events of the year, but with 68 teams competing across 14 cities over a period of nearly three weeks, wading through dozens of games and ticket packages to find the right seats is an exercise in tedium. Just about every site you could buy an NCAA Tournament ticket from displays these various packages as one big, ugly list, but we decided there had to be a better way.

Designing a new experience

The objective of this project was to reduce the friction of purchasing tournament tickets, and to do this, we needed to make the experience as streamlined and simple as possible. For March Madness, it seemed obvious that a bracket would offer the most intuitive experience.

But there was plenty of nuance to consider in the design of the bracket. Every decision about spacing, colors, size, information density, and interactivity affects its usability, and with so many events on the page at once, making the wrong design choice could throw off the balance and make it difficult to follow.

To keep this balance, only the absolute minimum amount of information is shown at any given time. Dates and locations are only shown for upcoming games, losing teams are grayed out to make it easier to scan through, and the “Tickets” calls to action are subtle yet clear.

Prototyping

From the very beginning, I knew that I wanted this bracket to be interactive in some way. I built a simple proof of concept to test out designs and interactions, as well as the underlying technology.

The games are set in place using CSS3’s new flexbox model. This makes the code much simpler, since it can automatically handle vertical centering and evenly distributed spacing of elements. But more importantly, it also allows the bracket to be responsive right out of the box, so it can work well on a variety of screen sizes without additional code or markup.

The connecting lines are drawn using Canvas, which offers a simple and high-performance way of rendering lines and polygons. This posed an interesting challenge and was the most difficult part to get right. Drawing lines and bezier curves is easy, but combining them in a way that is responsive is harder.

The S-curves between the round of 64 and the round of 32, for example, require four lines and two bezier curves. The points for each line segment and curve are calculated based on the position of each team. This example demonstrates how the connectors are drawn.

Iterating

Even though it worked well in that demo, the prototype still had several issues. Aside from messy code that’s difficult to maintain, the interactions (based entirely on jQuery) are bad for performance when scaling up to 63 games for a total of 126 teams rendered. Every time an element on a page is updated to, say, add a hover effect, it requires an expensive DOM update that slows down the page. This is difficult to optimize using just jQuery selectors and events.

Resolving this issue was easy thanks to React, Facebook’s reactive UI framework. React’s virtual document object model means that we can limit the number of times that a DOM update has to happen. That can even allow us to build complex interactive elements like this graphic in a relatively high-performance way without sacrificing usability.

Take a look at the live version of the bracket here.

Meet SeatGeek: Introducing Dallas Gutauckis

Name: Dallas Gutauckis

Role: Lead Android Engineer

Hometown: Port Orange, FL

Hypothetical SeatGeek talent show performance: Balancing things… such as a barstool on my finger, a hockey stick on my foot, etc.

Twitter handle: @dallasgutauckis

SGFL (SeatGeek Foosball League) Ranking: 2, 3? Either way, behind the undeniable king of foosball and sobriety, Ben Clark.

What was your path to SeatGeek?

This morning? ​I took the L train into 14 St/Union Square as I often do​ and then walked the few blocks uptown to the office; sometimes, I ride my bike. It’s a cool 7-mile ride.

I grew up programming. I started around age 9 with Visual Basic (whooo! … just kidding) and switched to web development combined with some basic C muddling. I was doing things like modifying game servers to be able to provide user scores to forum software and link accounts (in-game username being the same as the forum username, shared auth, credentials, etc.). From there I focused more on full-stack web development, doing contract work during high school and my first year of college. I dropped out of college to work at a startup in 2007 at a company called myYearbook. I worked on full-stack web development there for about 3½ years.

In 2010 I was attending Google I/O. That was the year they sent all of the attendees a Motorola Droid ahead of the conference and suggested they work on something and come with questions and whatnot to bring to the conference. So, with that, I made my first Android app. I essentially made Words with Friends for Android before it existed. (If you don’t know Words with Friends, think online multiplayer Boggle.) After that, my manager noticed I was doing Android development a lot in my spare time and asked if I’d be interested in working on the myYearbook Android app full-time. I obliged and ended up really loving it.

Our company was acquired in 2011. I stuck around for a while. Then, in late 2013/early 2014, Alex Blum and Jack Groetzinger both reached out to me from SeatGeek asking if I wanted to lead the Android team. It was really a perfectly awesome fit for me. I wanted to move to New York City, and I wanted some new things to work on. I couldn’t be happier with the decision I made to move.

What’s the most interesting project you’ve worked on since joining?

It’s not out yet, but we’re working on adding SeatGeek Checkout to the Android app. SeatGeek Checkout greatly simplifies the purchasing process for users by allowing them to save and re-use their payment information across many different ticket sellers. It’s an obvious win for users, and it’s also an obvious win for us. Checkout presents some exciting technical hurdles for us, especially as we’re working with new technologies like RxJava as well as ensuring we have solid test coverage for Checkout (something we really don’t want to break). We also have some fun new design and UX concepts we’re introducing to the app, so I just can’t wait to see it being used, and — we hope — loved!

What do you want to work on next?

I have really big ideas for revamping our search user experience and would really like to have a refined experience there. I’m hoping we’ll be able to do some design sprint work there as well — a process we haven’t tried out yet.

What’s your favorite SeatGeek experience?

As pretty much everyone at SeatGeek knows (it’s in my onboarding presentation​, as well as in every product presentation I give), I’m a huge fan of the Philadelphia Flyers. I really enjoyed going to the playoff games last year at Madison Square Garden against the Rangers; even after the Flyers lost to the Rangers and the Rangers were playing the Canadiens, I still went to those games with a colleague and had a blast. So, in summary, I’d say that the general experience of going to hockey games with our ticket perk is my favorite SeatGeek experience.

What’s your top NYC recommendation — food, fun, neighborhood, etc.?

I’m a huge fan of breakfast foods and sandwiches, but particularly breakfast sandwiches. Tompkins Square Bagels is one of my favorite places in the city. Mighty Quinn’s Barbeque gets a solid second. Any time a friend is visiting from out of town, I’m sure to take them to both.

What’s the most interesting thing you’ve read recently?

I read The Martian by Andy Weir recently. This book has definitely made the rounds in the tech/science fields, but also in general society (or so I’m told). I really liked the book. I didn’t actually read it, I listened to it on the L train. It’s a bit hard to follow when you’re listening to him rattle off numbers and equations, but I enjoyed it nonetheless. Apparently, there’s also a film scheduled for release in November of this year.

What’s been your favorite SeatGeek team event?

No offense to “Batsu!”, which we went to recently and had an awesome time at, but I enjoyed the hell out of the Arcade Fire concert we went to. The crowd was great, the band was great, the show was great, and hanging out with SeatGeeks was great. What more can I say?