He Said She Said

Shows how to use a QuantChannel to create real-time polling around video, a key social TV use case.

Download App Code

Click +User to see what it’s like with multiple users. Or just have a friend join you.

Who is right?


Sign In + User


number of active participants: loading...







App to Platform Traffic Log


To build He Said She Said, we will first create a QuantChannel. Then we’ll use the BrightContext API to push data (user information and votes) into the channel and get the processed results (aggregated vote values) out. Lastly, we’ll link these inputs and outputs into our app so that they power our vote value bars.

Set Up the QuantChannel

We made He Said She Said using a single QuantChannel which computes 7 aggregate values in real-time.

Let’s go step by step through creating the QuantChannel that powers He Said She Said.

Step 1. Create a new QuantChannel and name it.

Step 2. Define your Inputs.

This demo is structured to have one Input containing multiple fields. When you move the slider, you are creating messages with vote values attached to them and sending them into the system.  Let’s define He Said She Said’s inputs in our QuantChannel.

Step 3. Message Level Processing

He Said She Said doesn’t use any Message-Level Processing, so we can skip this step.

Step 4. Aggregation Processing

He Said She Said’s QuantChannel uses 7 aggregates. Let’s create them now:

Aggregate 1: userCount

We need to keep a count of all users on the system.


Now Aggregate 1 will keep count of all the users currently on the system:


Aggregate 2: avgVoteGlobal

We also need to know the average vote value of everyone participating.

Our second aggregate will tell us the average value of all the votes coming into the system, regardless of gender or age, to drive the Everyone bar:


Aggregate 3: avgVoteFemale

When it comes to screening out certain voters, we can leverage aggregation filters.


Now we have an aggregate that will average all incoming votes from women only:


Aggregate 4: avgVoteMale

The same principle applies for collecting votes from men only.

Now we can power the “Men” average vote display bar on the demo app:

Aggregate 5: avgVoteBracket1

When we defined our Inputs, we used Min/Max values of 1-3 to create three age brackets. By filtering on the ‘agebracket’ field with a value of 1, we can average only data from voters aged 18-34.

This aggregate will now drive the average vote value bar for our 18-34 age group:

Aggregate 6: avgVoteBracket2

We’ll do the same thing for our second age group.

Our 35-54 average vote value bar will now be powered by relevant votes:


Aggregate 7: avgVoteBracket3

Filtering ‘agebracket’ on 3 will only average votes from the 55+ demographic.

The last of our age-based average vote value bars is ready for data:

Step 5: Output

Our aggregates are ready to tabulate user data on the fly, but we also need to make their processed results available for our visualizations to use.


 Designing the Client App

Some basic page markup is needed to get started. A full explanation of jQuery, jQuery UI or Bootstrap is another topic, however it is important to understand how we gather the input data. To save time, we are using jQuery UI for the input slider and Bootstrap for the input drop downs and output progress meters. Here is the full vote input markup, it contains two drop downs to select age and gender, along with a slider control for voting.

      <div id="ddgender" data-value="M">
        <a data-toggle="dropdown" href="#">
          <li><a href="javascript:void(0)" data-value="M">Male</a></li>
          <li><a href="javascript:void(0)" data-value="F">Female</a></li>

      <div id="ddage" data-value="1">
        <a data-toggle="dropdown" href="#">
          <li><a href="javascript:void(0)" data-value="1">18-34</a></li>
          <li><a href="javascript:void(0)" data-value="2">35-55</a></li>
          <li><a href="javascript:void(0)" data-value="3">55+</a></li>


      <div class="voteslider"></div>

For the progress meters, we are using this animated bootstrap progress bar plugin that adds support for some fancy text labels and can be animated with JavaScript a bit easier.

  <div class="progress progress-info">
    <div id="stat-everyone" class="bar"></div>

You can use whatever input widgets or output visualizations you like in your code. Now that we have some UI widgets, we can wire up the user voting slider to our Input feed and the Ouput feed data to our progress bars. To use the platform on the web, we will need to import the SDK.

<script type="text/javascript" src="http://static.brightcontext.com/js-sdk/bcc.min.js"></script>

With the SDK ready, we need to tell the server who we are and what channel we’re using. We do that by providing our API key from the Settings panel along with the project and channel names. Because we are using a Web API Key, the HTTP Origin will be tested so that we can only use this API Key from our domain.

// our API Key from the BrightContext dashboard Settings screen
var apikey = 'ac09ca86-6121-4cce-ab7d-812d5dc00eb7';

// We recommend using one project per app to keep things organized
var projectname = 'He Said She Said';

// The name of our channel
// this is from the first step while setting up a Quant Channel
var channelname = 'calculator';

// A handle to our votes feed, we will be using this to send in our vote input
var votes = null;

Next we need to open the feeds. We will listen for the onopen event of the Input feed so that we know when it is ready to accept input from our user, and we will listen for the onmsgreceived event on the Output feed so we know when to update our meter visuals.

function openFeeds() {

  // initialize the context with our API key and open the project
  var p = BCC.init(apikey).project(projectname);

  // using the project, open the input feed
    channel: channelname,

    // the name of our Input when designing the Quant Channel
    name: 'votes', 

    onopen: function(f) {
      // when the feed is ready to be used, save a handle to it
      votes = f;

      // add an event handler that sends vote updates when the slider changes

  // using the same project, open the output feed
    channel: channelname,

    // the name of our Output when designing the Quant Channel
    name: 'stats',  

    onmsgreceived: function(f, msg) {
      // when we receive output calculations from the server, update our UI

To wire up the input slider widget to our input feed, we first need to initialize the slider, then capture the slide event. We do this once, when the feed is open and ready to use with wireupVoteSliderToFeed

function wireupVoteSliderToFeed(f) {
  $(".voteslider").bind("slidestop", function(evt, ui) {
    if (!vote) {

    example message contract
    votes = {
       gender : 'String',
       agebracket : 'String',
       vote : 10

    // gather our UI data to build our message
    var g = $("#ddgender").attr('data-value');
    var a = $("#ddage").attr('data-value');
    var v = ui.value;

    // because we have left message contract checking ON
    // we call parseInt() to change our strings from the UI into numbers
    var msg = {
        gender : g,
        agebracket : a,
        vote : parseInt(v)

    // send the message on the input feed

When our Output feed receives messages, they are the results of the processing we designed in our Quant Channel. The drawFeedStats function simply animates the progress bars to show the current server calculations in real time.

function drawFeedStats(msg) {
  example msg object:
    numberofusers : 12345,
    avg_everyone : 1.2345,
    avg_f : 1.2345,
    avg_m : 1.2345,
    avg_young : 1.2345,
    avg_old : 1.2345,
    avg_retired : 1.2345

  // update the current number of users in the app

  // update the calculated aggregates
  $('#stat-everyone').attr({ 'data-percentage': Math.round(msg.avg_everyone) });
  $('#stat-women').attr({ 'data-percentage': Math.round(msg.avg_f) });
  $('#stat-men').attr({ 'data-percentage': Math.round(msg.avg_m) });
  $('#stat-young').attr({ 'data-percentage': Math.round(msg.avg_young) });
  $('#stat-old').attr({ 'data-percentage': Math.round(msg.avg_old) });
  $('#stat-retired').attr({ 'data-percentage': Math.round(msg.avg_retired) });

  // animate the new bars into position
  $('.progress .bar').progressbar(meter_animation);


While this demo looks very basic, it’s important to note that it can scale to hundreds of thousands of concurrent users seamlessly by leveraging our flexible load balancing cloud infrastructure.  You can take this same principle and use BrightContext QuantChannels to build the next generation of social TV and audience participation apps.