Introduction

These tutorials are for Interactive 1, Interactive 2 tutorials are coming soon! While you wait, check out our Interactive 2 reference docs.

Our interactive platform communicates using Google's Protocol Buffer specification over a standard secure websocket protocol (wss) connection. Viewers' inputs from the site are sent to Mixer's aggregator server, which analyzes them for you, before being sent to your Robot.

Registering a Game On Mixer

Head over to the Developer Lab and click the 'Interactive Games' tab. Click the plus button next to 'My Games', and name the game whatever you'd like. Hit 'Create Game'.

You’re now in the control editor. For our simple robot, we just want to be able to record joystick input. Click the Joystick Tab on the right side of the screen and then click 'New'.

Expand the joystick's settings by clicking the arrow on the right of your new joystick and check the 'Coords (Mean)' analysis. Drag the Joystick onto the control grid on the left.

The final result should look like this:

Interactive joystick configuration

Everything else can be left as default. Click Save. We're finished with the control editor. Let's get started on writing the code!

Writing the Code

We’re going to be using our Node, Java and Python to build a simple Robot. Users will be able to visit your channel on Mixer, and control the mouse movement on your screen.

We'll be using OAuth for authentication. In the tutorial code below, click "Click here to get your token" to grab a token for the tutorial. You can read more about how OAuth works on our OAuth reference page

Prerequisites

  1. Get NodeJS and NPM for your platform.
  2. Create a new project with npm.
  3. Run npm i -S beam-client-node beam-interactive-node robotjs

Usage

Let's start by importing all of the modules which we'll need and define our channel id.

You can find your channel id by going to https://mixer.com/api/v1/channels/username?fields=id in your browser, replacing username with your Mixer username.

const Beam = require('beam-client-node');
const Interactive = require('beam-interactive-node');
const rjs = require('robotjs');

const channelId = 1234;

To connect to Mixer Interactive we need to authenticate as our user specified above using beam-client-node we'll use an implicit OAuth token to do this. The required scopes are interactive:robot:self. Once authenticated we're asking the api to join a channel as a robot with beam.game.join. We'll use the response from this to create a robot in the next step.

const beam = new Beam();

beam.use('oauth', {
    tokens: {
        access: 'Click here to get your Token!',
        expires: Date.now() + (365 * 24 * 60 * 60 * 1000)
    },
});

beam.request('GET', `users/current`)
.then(() => beam.game.join(channelId))
.then(res => createRobot(res))
.then(robot => performRobotHandShake(robot))
.then(robot => setupRobotEvents(robot))
.catch(err => {
    if (err.res) {
        throw new Error('Error connecting to Interactive:' + err.res.body.message);
    }
    throw new Error('Error connecting to Interactive', err);
});

The join request should resolve with something like this

{
    "address": "wss://tetris2-dal.mixer.com",
    "key": "3l39jjgnhqf4kv4"
}

We can use these details to create a robot by writing the createRobot function.

function createRobot(res, stream) {
    return new Interactive.Robot({
        remote: res.body.address,
        channel: channelId,
        key: res.body.key,
    });
}

Once we have a robot we need to perform a handshake with the interactive servers and then hook up event handlers so that a joystick on beam is bound to your mouse. We do this by creating the performRobotHandShake and setupRobotEvents functions.

function performRobotHandShake (robot) {
    return new Promise((resolve, reject) => {
        robot.handshake(err => {
            if (err) {
                reject(err);
            }
            resolve(robot);
        });
    });
}

function setupRobotEvents (robot) {
    robot.on('report', report => {
        const mouse = rjs.getMousePos();
        if (report.joystick.length > 0) {
            const mean = report.joystick[0].coordMean;
            if (!isNaN(mean.x) && !isNaN(mean.y)) {
                rjs.moveMouse(
                    Math.round(mouse.x + 300 * mean.x),
                    Math.round(mouse.y + 300 * mean.y)
                );
            }
        }
    });
    robot.on('error', err => {
        throw new Error('There was an error in the Interactive connection', err);
    });
}

This function takes our Robot instance and initiates a handshake, An error will be reported if a problem occurs during this process. Once its complete you'll get regular reports from Mixer as a report event on the robot object.

As we've only registered a Joystick control on Mixer we'll only get joystick updates. These come in an array within the report. As we've selected mean, the mean of all the viewers joysticks will be contained within the coordMean object. We take this mean and apply it to the mouse position. The final code can be found below:

const Beam = require('beam-client-node');
const Interactive = require('beam-interactive-node');
const rjs = require('robotjs');

const channelId = 1234;

const beam = new Beam();

beam.use('oauth', {
    tokens: {
        access: 'Click here to get your Token!',
        expires: Date.now() + (365 * 24 * 60 * 60 * 1000)
    },
});

beam.request('GET', `users/current`)
.then(() => beam.game.join(channelId))
.then(res => createRobot(res))
.then(robot => performRobotHandShake(robot))
.then(robot => setupRobotEvents(robot))
.catch(err => {
    if (err.res) {
        throw new Error('Error connecting to Interactive:' + err.res.body.message);
    }
    throw new Error('Error connecting to Interactive', err);
});

function createRobot (res) {
    return new Interactive.Robot({
        remote: res.body.address,
        channel: channelId,
        key: res.body.key,
    });
}

function performRobotHandShake (robot) {
    return new Promise((resolve, reject) => {
        robot.handshake(err => {
            if (err) {
                reject(err);
            }
            resolve(robot);
        });
    });
}

function setupRobotEvents (robot) {
    robot.on('report', report => {
        const mouse = rjs.getMousePos();
        if (report.joystick.length > 0) {
            const mean = report.joystick[0].coordMean;
            if (!isNaN(mean.x) && !isNaN(mean.y)) {
                rjs.moveMouse(
                    Math.round(mouse.x + 300 * mean.x),
                    Math.round(mouse.y + 300 * mean.y)
                );
            }
        }
    });
    robot.on('error', err => {
        throw new Error('There was an error in the Interactive connection', err);
    });
}

Prerequisites

  1. Java 1.8 or above
  2. A Java IDE such as:
    1. Eclipse
    2. IntelliJ
    3. NetBeans
  3. A Java Project Manager such as:
    1. Maven
    2. Gradle

Project Setup

Setup a standard project for your environment and include beam-client-java and beam-interactive-java as dependancies.

To setup beam-client-java and beam-interactive-java, first add the Mixer repo to your pom.xml as a repository as follows:

<repositories>
  <repository>
    <id>beam-snapshots</id>
    <url>https://maven.mixer.com/content/repositories/snapshots/</url>
  </repository>
</repositories>

And secondly, add this project as a dependency in your pom.xml:

<dependencies>
  <dependency>
    <groupId>pro.beam</groupId>
    <artifactId>api</artifactId>
    <version>3.0.3-SNAPSHOT</version>
  </dependency>
  <dependency>
    <groupId>pro.beam</groupId>
    <artifactId>interactive</artifactId>
    <version>1.5.1-SNAPSHOT</version>
  </dependency>
</dependencies>

To setup beam-client-java and beam-interactive-java, first add the Mixer repo to your build.gradle as a repository as follows:

repositories {
    maven {
        name = "beam"
        url = "https://maven.mixer.com/content/repositories/snapshots"
    }
}

And secondly, add this project as a dependency in your build.gradle:

dependencies {
    compile "pro.beam:api:3.0.3-SNAPSHOT"
    compile "pro.beam:interactive:1.5.1-SNAPSHOT"
}

Usage

Let's start by creating a Main class for the Java application and importing all of the required packages. We'll also initialize a few objects. A instance of the MixerAPI and an instance of the AWT Robot class which will allow us to control the mouse.

package pro.beam.interactive.example;

import pro.beam.api.BeamAPI;
import pro.beam.interactive.net.packet.Protocol;
import pro.beam.interactive.robot.RobotBuilder;

import java.awt.*;
import java.util.concurrent.ExecutionException;

public class Main {

    public static void main(String[] args) throws AWTException {
        BeamAPI beam = new BeamAPI("Click here to get your Token!"); //An instance of the BeamAPI from beam-client-java
        Robot controller = new Robot(); // An AWT Robot, Not a Beam Robot
    }
}

Next we need to setup a Mixer Interactive robot and supply it with valid beam account credentials and a channel id.

You can find your channel id by going to https://mixer.com/api/v1/channels/username?fields=id in your browser, replacing username with your Mixer username.

pro.beam.interactive.robot.Robot robot = new RobotBuilder()
    .channel(user.channel)
    .build(beam, false)
    .get();

The last thing to do is wire up a report handler that takes the joystick's coordMean and applys it to the mouse.

robot.on(Protocol.Report.class, report -> {
    // If we have any joysticks in the report
    if (report.getJoystickCount() > 0) {
        // Get the coordMean from the joystick
        Protocol.Coordinate coordMean = report.getJoystick(0).getCoordMean();
        Point mousePosition = MouseInfo.getPointerInfo().getLocation();

        // Apply it to the current mouse position, if its values are not NaN
        if (!Double.isNaN(coordMean.getX()) && !Double.isNaN(coordMean.getY())) {
            controller.mouseMove(
                ((int) (mousePosition.getX() + 300 * coordMean.getX())),
                ((int) (mousePosition.getY() + 300 * coordMean.getY()))
            );
        }
    }
});

When you put everything together you get the final code which is below:

package pro.beam.interactive.example;

import pro.beam.api.BeamAPI;
import pro.beam.interactive.net.packet.Protocol;
import pro.beam.interactive.robot.RobotBuilder;

import java.awt.*;

import java.util.concurrent.ExecutionException;

public class Main {

    public static void main(String[] args) throws AWTException {
        BeamAPI beam = new BeamAPI("Click here to get your Token!");
        Robot controller = new Robot();
        BeamUser user = beam.use(UsersService.class).getCurrent().get();
        try {
            pro.beam.interactive.robot.Robot robot = new RobotBuilder()
                .channel(user.channel)
                .build(beam, false)
                .get();

            robot.on(Protocol.Report.class, report -> {
                // If we have any joysticks in the report
                if (report.getJoystickCount() > 0) {
                    // Get the coordMean from the joystick
                    Protocol.Coordinate coordMean = report.getJoystick(0).getCoordMean();
                    Point mousePosition = MouseInfo.getPointerInfo().getLocation();

                    // Apply it to the current mouse position, if its values are not NaN
                    if (!Double.isNaN(coordMean.getX()) && !Double.isNaN(coordMean.getY())) {
                        controller.mouseMove(
                            ((int) (mousePosition.getX() + 300 * coordMean.getX())),
                            ((int) (mousePosition.getY() + 300 * coordMean.getY()))
                        );
                    }
                }
            });

        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Start Streaming

Head over to your channel page and click 'Go Interactive' on the bottom of the page. Now find your game and click the 'Play' button.

You should now see "Waiting for interactive app to connect. Make sure to launch it!". At this point start your project through the usual method for your chosen language.

If everything has gone well you should now see a joystick underneath your channel’s stream. Move the joystick and your mouse should move in that direction.

Want More Info?

If you'd like more information on our Interactive system, check out the reference guide.

Need more help?

If you're still not sure, or would like some help, hit us up on Gitter!