COCOROBIRDMAN、モノに “心” を授ける!?
会話デバイス『COCORO』完成!

BIRDMAN

BIRDMAN、モノに “心” を授ける!?
会話デバイス『COCORO』完成!

Play!

「Intel® Edison」× 日本屈指の表現者たちーー両者の融合から始まる、かつてない “未来体験” へ、ようこそ!
8/1(土)、2(日)の2日間にわたって開催された、最先端の “ものづくり” の祭典『Maker Faire Tokyo 2015』(MFT 2015)。その一角、Intelブースの目玉企画のひとつとして、最前線の作り手たちによる展示企画『Artist Showcase』の作品が、今年もその全貌を現した。
過去3回にわたって5組の参加アーティストたちに密着し、発想のヒントや完成へと至る道のりをお届けしてきたこのレポート。
ついにお披露目を迎えた彼らの作品は、MFT 2015の会場でどんな光を放ったのか。彼らのチャレンジ精神と最新テクノロジーにかける想いを、システム構成やソースコードとともに解き明かしていこう……!

ぬいぐるみ、冷蔵庫……
キャラ立ち十分の会話が
楽しめる!

Intelブースの一角に忽然(こつぜん)と現れた、ソファや冷蔵庫、テレビ、観葉植物などが置かれたリビングルーム。
じつはこれ全体が、気鋭のインタラクティブスタジオBIRDMANによる展示空間なのだ。ヒントは、やがて訪れる未来の暮らし。IoT化によって、モノに心が芽生えたなら、一体どうなる!?
そこで、彼らがEdisonを使って作り上げたハート型デバイス『COCORO』が登場。
『COCORO』を装着したオランウータンのぬいぐるみは、「え〜とねえ、あのねえ……」と、無邪気な子どもの声で返事をしてくれる。冷蔵庫やソファも、「ハハハハハ」と笑うシニカルな紳士や、ちょっとトボけた老人など、それぞれのキャラクターで会話がスタート。
驚きの “モノと会話が楽しめるハート型デバイス” 、その手応えはいかに……?

コバヤシタケル
オランウータンのぬいぐるみの「〜でござる」という口調や、ソファじいさんの「ほっほっほっ」という笑い声など、それぞれにキャラクター性の高い会話を楽しんでもらえる作品に仕上がったと思います。Edisonの通信機能を活かして会話APIで生成された言葉に、キャラごとに辞書を設定してオリジナリティを出しているんです。
久野勇人
この会話や声の細かい調整に、かなり苦労しました。無数の音が飛び交う空間でうまく声を認識するように、会場に着いてからもずっと調整を続けていました。
コバヤシ
久野くんは開発を驚くほどスムーズにこなしてくれていたので、僕としては「これがイベントの現場の厳しさだ!」ということを伝える、新人教育の絶好の機会になったと思っています。……って、ハードとソフトのつなぎ込みの部分も含めて、2人とも展示直前は徹夜続きの状態だったわけですが……。でも、例えばハードの部分では、内部が見えるスケルトン仕様のデザインにぜひ注目してほしい。予想通り、ハートの形状にEdisonや自作の基盤を収めるのに苦労しましたが、レーザーカッターで切り出したケース部分も含めて、美しく仕上がったと思います。

モノと会話が楽しめるハート型デバイス
『COCORO』の仕組みを大公開!

Note: オランウータン、冷蔵庫、ソファは、それぞれ語彙や話し方のトーンに個性がある。ここでは、オランウータン(monkey)の例を用いている。

中心となるプログラムと同様、子プロセスとなる部分も Node.js に書かれている。

『COCORO』に誰かが近づくと距離センサーで感知し、LEDが点灯。接続されたマイクからの音声を録音し始め、それを一時的にローカルフォルダに保存する。話者の話が終わると、録音も終了する。

プログラムは一番近くにあるBluetoothのBeacon(ビーコン)を確認する。今回はオランウータンのぬいぐるみ、冷蔵庫、テレビ、テーブル、照明、プラント、ソファに取り付けられた、それぞれのデバイスはBluetooth のBeacon を持っており、それぞれのUDIDが各キャラクター特定のオーディオファイルに応答するようになっている。(例: output_dialogue_ da41e595b25f4248919813bc80baedc5.raw )
そして、人の声を感知すると、そのオーディオファイルがプレイバックされる。オランウータンであれば、例の「え〜とねえ」がそれに当たる。

録音されたオーディオは処理・圧縮され、クラウドのPHPプログラムに Edison のWi-Fiを介して送られる。そして、このPHPプログラムが音声を文字に変換する Google Speech API にデータを送信する 。Google Speech APIから文字となったデータは 再度 PHPプログラムを通して、人工知能会話サービスで知られる SimSimi APIに送られる。ここからの返答は、またPHPプログラムを介して、さらに Docomo AITalkAPI で音声ファイルに書き換えられる。そのデータが Edison のWi-Fiを通じて戻り、スピーカーを通して言葉となって発生される。これによってモノとの会話が可能になるのだ。

Wi-Fiを通してクラウドにあるPHPプログラムとデータの送受信をし、3つのAPIを通す必要があるため、開発する上で一番のネックとなった部分は応答するまでの時間だという。この間を埋めるため、事前に録音されたオーディオ(「え〜とねえ」など)を意図的にゆっくりプレイさせた。ゆっくり発せられる言葉が終わる頃には AITalk APIからの結果を受け取り、会話への回答がプレイできる仕組みだ。東京ビックサイトのMFT会場ではワイヤレスネットワークが混雑していたこともあり、状況は通常より厳しかったようだ。

上記に加え、さらなるチャレンジは、独自に作った小さいハート型の基板にすべての技術を詰めこまなければいけなかった部分だったが、完成品は実に見栄えも良かった。普段は何も言葉を発するはずのないモノが『COCORO』によって心と個性を与えられるというチャレンジは、見事に実現した。

材料

クラウドサービス

  • Google Speech API
  • SimSimi AI conversation program API
  • Docomo AITalk API

ソースコード

Learn More on GitHub

main.js
                var voiceThreshold = 2000;

var Mraa = require('./model/Mraa.js');
var AudioIn = require('./model/AudioIn.js');
var AudioOut = require('./model/AudioOut.js');
var Cocoro = require('./model/CocoroAPI.js');
var Beacon = require('./model/ReceiveBeacons.js');

var isRecording = false;
var isRecordInit = false;
var isSpeaking = false;

var fps = 10;
var quietCount = 0;

var isDeviceMoving_prev = false;

function setup() {

  AudioIn.setup();
  AudioIn.setDebugVU(true);
  AudioIn.setSpeakThreshold(voiceThreshold);
  Mraa.setLed('ON_RED');
  console.log('\n / ̄\/ ̄\\n|     |\n \  OK  /\n   \  /\n     \/\n');
  console.log("-- MIC START");

  var loopSystem = setInterval(function() {
    update();
  }, 1000/fps);

}

function update(){

  var isSpeaking = AudioIn.getIsSpeaking();

  if(isSpeaking == true && isRecordInit == false) {
    console.log("-- Recognized the voice");
    AudioIn.saveRecCache('/home/root/cocoro/sound/record.wav');
    isRecording = isRecordInit = true;
  } 

  if(isRecording == true) {
    if(isSpeaking == true) {
      Mraa.setLed('ON_RED');
      quietCount = 0;
    }
    else if(isSpeaking == false) {
      if(quietCount == 4) {
        console.log("-- MIC END");
        isRecording = false;
        AudioIn.setPause(true);
        var nearestBeacon = Beacon.getNearestPeripheral();
        Cocoro.aizuchi(nearestBeacon, function(){
          Mraa.setLed('ON_BLUE');
          AudioOut.playSound('/home/root/cocoro/sound/output_aizuchi_'+nearestBeacon+'.raw', 16000);
        });
        Cocoro.dialogue('/home/root/cocoro/sound/record.wav', nearestBeacon, function(){ 
          AudioOut.playSound('/home/root/cocoro/sound/output_dialogue_'+nearestBeacon+'.raw', 16000, function(){ 
            restartListen();
          });
        });
      } else {
        quietCount++;
      }
    }
  }

}

function restartListen(){

  Mraa.setLed('ON_RED');
  console.log("-- MIC START");
  isRecording = isRecordInit = false;
  quietTimer = 0;
  AudioIn.setPause(false);

}

setup();
                
              
AudioIn.js
                var mic = require('microphone');
var fs = require('fs');

var isDebug_vu = false;

// 16bit Little Endian 16000 mono
var wavHeader = new Buffer([0x52,0x49,0x46,0x46,0x24,0x00,0x00,0x80,0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x80,0x3e,0x00,0x00,0x00,0xee,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61,0x00,0x00,0x00,0x80]);
var hpfCoef = [ 0.0016976527263135508, 0.0019472525434874582, 0.0023725160960826014, 0.0026170653011860535, 0.002076356271734383, 1.0875899412729064e-17, -0.004348222178476784, -0.011504574872912099, -0.02163457787571366, -0.034417846740109885, -0.049019722472303774, -0.06416377602741553, -0.07829819320310744, -0.08982857267499483, -0.09737440758415258, 0.9, -0.09737440758415258, -0.08982857267499483, -0.07829819320310744, -0.06416377602741553, -0.049019722472303774,  -0.034417846740109885,  -0.02163457787571366, -0.011504574872912099,  -0.004348222178476784, 1.0875899412729064e-17, 0.002076356271734383, 0.0026170653011860535,  0.0023725160960826014,  0.0019472525434874582, 0.0016976527263135508 ];
var recordData = [];
var volume = 0.00;
var speakThreshold = 7000;

var isActive = false;
var isPause = false;
var isSpeaking = false;

exports.setup = function() {
  isActive = true;
  fs.writeFile('/home/root/cocoro/sound/record.wav', wavHeader);
  mic.startCapture();
};

mic.audioStream.on('data', function(data) { 
  if(data.length>=44 && isActive==true) loop(data);
});

function loop(data){

  var compressData = compressPCM(data);
  var dataBuf = new Buffer(compressData);

  setRecordData(compressData);
  setVolume(data);
  fs.appendFile('/home/root/cocoro/sound/record.wav', dataBuf);

}

function compressPCM(buf){
 
  // 48000hz 16Bit stereo -> 16000hz 16Bit mono
  var passing = 4*(48000/16000);
  var compressBuf = [];
  for(var i = 0; i 16000*1) {
    recordData = recordData.slice(recordData.length-16000*1, recordData.length);
  }

}

function setVolume(buf){ 
  
  // NOTE: 'MAX_WAVE' / 'AVERAGE_WAVE'
  var mode = 'AVERAGE_WAVE'; 

  var gain = 18.00;
  var passing = 2*16;
  var decimalArr = [];
  var decimalFilter = [];
  var hpfLen = hpfCoef.length;
  var waveMax = 0;
  var waveAvarage = 0;
  
  for(var i=0; i < buf.length; i+=passing){
    var a = buf[i].toString(16);
    if(a.length == 1) a = "0" + a;
    var b = buf[i+1].toString(16);
    if(b.length == 1) b = "0" + b;
    var str = b + a; // 16bit - Little Endian
    var decimalNum = parseInt(str ,16);
    if(decimalNum > 32767) decimalNum -= 65536;
    decimalArr.push(decimalNum);
  }

  for (var i=0; i < decimalArr.length; i++) { 
    decimalFilter[i] = 0.00;
    if(i < hpfLen) continue;
    for(var j=0, k=i; j < hpfLen; j++, k--){ 
      decimalFilter[i] += decimalArr[k] * hpfCoef[j];
    }
    waveAvarage += Math.abs(decimalFilter[i]);
    if(Math.abs(decimalFilter[i])>waveMax) waveMax = Math.abs(decimalFilter[i]);
  }
  waveAvarage *= (0.0001*gain);

  if(isDebug_vu == true && isPause == false) {
    var vu = '';
    if(mode == 'MAX_WAVE'){
      for(var i=0; i speakThreshold) speaking = true;
  else speaking = false;
  return speaking;

};

exports.setPause = function(f){
  isPause = f;
};

exports.resetFile = function(file){
  fs.writeFile(file, wavHeader);
};

exports.saveRecCache = function(file){
  var a = recordData;
  console.log("test",recordData.length, recordData_new.length, a.length);
  var buf = new Buffer(a);
  fs.writeFile(file, wavHeader);
  fs.appendFile(file, buf);
};

exports.saveRecStream = function(file){
  var buf = new Buffer(recordData);
  fs.appendFile(file, buf);
};

exports.setDebugVU = function(f){
  isDebug_vu = f;
};
                
              
AudioOut.js
                var exec = require('child_process').exec;
var speechText = '';

exports.playSound = function(file, freq, callback) {
  setTimeout(function(){
  exec('sox -v 1 -r 48k -e signed -b 16 -c 2 --endian big ' + file + ' /home/root/cocoro/sound/file_addHeader.wav', function(error, stdout, stderr){
  	exec('sox /home/root/cocoro/sound/file_addHeader.wav /home/root/cocoro/sound/file_lowspeed.wav speed 0.166', function(error, stdout, stderr){
  		exec('aplay -f S16_LE -c 2 -D hw:1,0 /home/root/cocoro/sound/file_lowspeed.wav');
  		exec('wc -c /home/root/cocoro/sound/file_lowspeed.wav | cut -d " " -f1 -', function(error, stdout, stderr){
    		var delay = 0;
    		delay = Math.floor(stdout/158);
    		setTimeout(callback, delay);
  		});
  	});
  });
  }, 500);
};

exports.playSoundAizuchi = function(file, freq, callback) {
  //exec('aplay -D sysdefault:CARD=U0x41e0x30d3 -r ' + freq + ' -c 1 -f S16_BE ' + file);
  exec('sox -v 1 -r 48k -e signed -b 16 -c 2 --endian big ' + file + ' /home/root/cocoro/sound/file_addHeaderAizuchi.wav', function(error, stdout, stderr){
    exec('sox /home/root/cocoro/sound/file_addHeaderAizuchi.wav /home/root/cocoro/sound/file_lowspeedAizuchi.wav speed 0.166', function(error, stdout, stderr){
      exec('aplay -f S16_LE -c 2 -D hw:1,0 /home/root/cocoro/sound/file_lowspeedAizuchi.wav');
      exec('wc -c /home/root/cocoro/sound/file_lowspeedAizuchi.wav | cut -d " " -f1 -', function(error, stdout, stderr){
        var delay = 0;
        delay = Math.floor(stdout/158);
        setTimeout(callback, delay);
      });
    });
  });
};
                
              
CocoroAPI.js
                var exec = require('child_process').exec;
var fs = require('fs');

exports.dialogue = function(inputFile, beaconID){
  exec(
    "curl -X POST --data-binary @" + inputFile + " http://://****.***/cocoro/v1/dialogue/" + beaconID + ".php",
    function(error, stdout, stderr){
      exec( "wget http://****.***/cocoro/v1/dialogue/output_dialogue_" + beaconID + ".raw -N -P /home/root/cocoro/sound", function(){ console.log(stdout); });
    }
  });
};

exports.aizuchi = function(beaconID, callback){
  exec(
    "curl -X POST -d beacon=" + beaconID + " http://****.***/cocoro/v1/aizuchi/index.php",
    function(error, stdout, stderr){
      console.log(stdout);
      exec( "wget http://****.***/cocoro/v1/aizuchi/output_aizuchi_" + beaconID + ".raw -N -P /home/root/cocoro/sound", function(){ callback(); });
    }
  );
};
                
              
Mraa.js
                var m = require('mraa');

var led_red = new m.Gpio(20);
var led_green = new m.Gpio(14);
var led_blue= new m.Gpio(0);

led_red.dir(m.DIR_OUT);
led_green.dir(m.DIR_OUT);
led_blue.dir(m.DIR_OUT);

exports.setLed = function(color){

  if(color == 'ON_GREEN') {
    led_red.write(0);
    led_green.write(1);
    led_blue.write(0);
  } else if(color == 'ON_RED') {
    led_red.write(1);
    led_green.write(0);
    led_blue.write(0);
  } else if(color == 'ON_BLUE'){
    led_red.write(0);                             
    led_green.write(0);          
    led_blue.write(1);  
  } else if(color == 'ON_YELLOW') {                             
    led_red.write(1);                                        
    led_green.write(1);                                   
    led_blue.write(0);                            
  } else if(color == 'OFF') {
    led_red.write(0);
    led_green.write(0);
    led_blue.write(0);
  }
  ledColor = color;

}

exports.getLed = function(){
  return ledColor;
}

function char(x) { return parseInt(x, 16); }
                
              
ReceiveBeacons.js
                Bleacon = require('bleacon');
Bleacon.startScanning();

var nearestPeripheral;
Bleacon.on('discover', function(bleacon) {
  if(bleacon.uuid == 'da41e595b25f4248919813bc80baedc5' && bleacon.major == 6 && bleacon.minor  == 1 ){
  	if(bleacon.proximity == 'immediate') nearestPeripheral = 'monkey';
  } else if(bleacon.uuid == 'da41e595b25f4248919813bc80baedc5' && bleacon.major == 6 && bleacon.minor  == 2) {
  	if(bleacon.proximity == 'immediate') nearestPeripheral = 'fridge';
  } else if(bleacon.uuid == 'da41e595b25f4248919813bc80baedc5' && bleacon.major == 6 && bleacon.minor  == 3) {
  	if(bleacon.proximity == 'immediate') nearestPeripheral = 'tv';
  } else if(bleacon.uuid == 'da41e595b25f4248919813bc80baedc5' && bleacon.major == 6 && bleacon.minor  == 4) {
  	if(bleacon.proximity == 'immediate') nearestPeripheral = 'table';
  } else if(bleacon.uuid == 'da41e595b25f4248919813bc80baedc5' && bleacon.major == 6 && bleacon.minor  == 5) { 
    if(bleacon.proximity == 'immediate') nearestPeripheral = 'light';           
  } else if(bleacon.uuid == 'da41e595b25f4248919813bc80baedc5' && bleacon.major == 6 && bleacon.minor  == 6) {
    if(bleacon.proximity == 'immediate') nearestPeripheral = 'plant';
  } else if(bleacon.uuid == 'da41e595b25f4248919813bc80baedc5' && bleacon.major == 6 && bleacon.minor  == 7) { 
    if(bleacon.proximity == 'immediate') nearestPeripheral = 'sofa';           
  } 
});

exports.getNearestPeripheral = function(){
  if(nearestPeripheral=='monkey'|nearestPeripheral=='fridge'|nearestPeripheral=='tv'|nearestPeripheral=='table'|nearestPeripheral=='light'|nearestPeripheral=='plant'|nearestPeripheral=='sofa'){
    console.log("NEAR BEACON: "+nearestPeripheral);
  } else {
    console.log("NOT BEACON");
    nearestPeripheral = 'monkey';
  }
	return nearestPeripheral;
}
                
              
monkey.php
                
<?php
  $input_binary = file_get_contents("php://input");
  //google speech
  $APIKEY = '***********';
  $requestUrl = 'https://www.google.com/speech-api/v2/recognize?output=json&client=chromium&maxresults=1&lang=ja&key='.$APIKEY;
  $lang = 'ja-jp';
  $output  'json';
  $contentType = 'Content-Type: audio/l16; rate=16000;';
  $curl=curl_init($requestUrl);
  curl_setopt($curl,CURLOPT_POST, TRUE);
  curl_setopt($curl,CURLOPT_HTTPHEADER, array($contentType));
  curl_setopt($curl,CURLOPT_POSTFIELDS, $input_binary);
  curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, FALSE);
  curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, FALSE); 
  curl_setopt($curl,CURLOPT_RETURNTRANSFER, TRUE);
  curl_setopt($curl,CURLOPT_FOLLOWLOCATION, TRUE); 
  $output = curl_exec($curl);
  // parse JSON
  $obj = json_decode($output, true);
  $result_speech = $obj['result'][0]['alternative'][0]['transcript'];
  echo $result_speech;
  $json = file_get_contents('./profile/monkey.json');
  $arr = json_decode($json, true);
  $answer = '';
  if(strpos($result_speech, 'はじめまして') !== false ) $answer = $arr['HAJIMEMASHITE'];
  else if(strpos($result_speech, 'おはよ') !== false) $answer = $arr['OHAYO'];
  else if(strpos($result_speech, 'こんにちは') !== false) $answer = $arr['KONNICHIWA'];
  else if(strpos($result_speech, 'ばいばい') !== false) $answer = $arr['BYEBYE1'];
  else if(strpos($result_speech, 'さよなら') !== false) $answer = $arr['BYEBYE2'];
  else if(strpos($result_speech, 'さようなら') !== false) $answer = $arr['BYEBYE3'];
  else if(strpos($result_speech, 'ありがと') !== false) $answer = $arr['ARIGATO'];
  else if(strpos($result_speech, '名前') !== false) $answer = $arr['NAME'];
  else if(strpos($result_speech, '性別') !== false) $answer = $arr['SEX'];
  else if(strpos($result_speech, '年齢') !== false) $answer = $arr['AGE'];
  else if(strpos($result_speech, '何歳') !== false) $answer = $arr['AGE'];
  else if(strpos($result_speech, '出身') !== false) $answer = $arr['COME_FROM'];
  else if(strpos($result_speech, '誕生日') !== false) $answer = $arr['BIRTHDAY'];
  else if(strpos($result_speech, '血液型') !== false) $answer = $arr['BLOOD'];
  else if(strpos($result_speech, '趣味') !== false) $answer = $arr['HOBBY'];
  else if(strpos($result_speech, '特技') !== false) $answer = $arr['SKILL'];
  else if(strpos($result_speech, '夢') !== false) $answer = $arr['DREAM'];
  else if(strpos($result_speech, 'フェチ') !== false) $answer = $arr['FETISH'];
  else if(strpos($result_speech, 'インテル') !== false) $answer = $arr['INTEL'];
  
  if($answer == '') {
    // simsimi API
    $api_key = '***********';
    $result_speech_encode = urlencode($result_speech);
    $api_url = 'http://sandbox.api.simsimi.com/request.p?key='.$api_key.'&lc=ja&ft=1.0&text='.$result_speech_encode;
    $res = file_get_contents($api_url);
    if(strpos($res, '"result":400') === false) { 
      $first = strrpos ( $res , "se\":\"" )+5;
      $last = strrpos ( $res , "\",\"id\"" )-$first;
      $answer = substr( $res, $first, $last);
    } else { // SimSimi or googlespeech error
      $input = array(
        'もう少し近づいてしゃべってください','ごめんなさい、もう少し近くでしゃべれますか?','マイクのすぐそばでしゃべってください'
      );
      $rand_keys = rand(0, 2);
      $answer = $input[$rand_keys];
    } 
    $search = array('私','わたし','ワタシ','僕','ぼく','ボク','俺','おれ','オレ','ウチ','自分');
    $answer = str_replace($search,'拙者',$answer);
    $search = array('
','です','だよ','*');
    $answer = str_replace($search,'',$answer);
    $answer = $answer.'でござる';
  }
  $xml = ''.$answer.'';
  $url = 'https://api.apigw.smt.docomo.ne.jp/aiTalk/v1/textToSpeech?APIKEY=394d5258537052766a52586a697836365a734c5a426a59596c325871456730337133684863324964617a38';  
  $headers = array(
    'Content-Type:application/ssml+xml',
    'Accept:audio/L16',
    'Content-Length:'.sprintf('%d',strlen($xml))
  );
  $stream = stream_context_create(array('http' => array(
        'method' => 'POST',
        'header' => implode('
',$headers),
        'content' => $xml
      )));
  $outputpath = 'output_dialogue_a.raw';
  $a = file_get_contents($url, false, $stream);
  file_put_contents($outputpath, $a);
  echo $answer;
?>                
              

「イメージとしては、ドラえもんの道具みたいに近くにあるものにくっつけたら、それが突然しゃべりだす感じ」(コバヤシ)
「どんなものにも簡単に装着できる点こそ、いちばん画期的なところだと思っています」(久野)
(※いずれもこれまでの『Artist Showcase』密着記事より)
気鋭のインタラクティブスタジオのエース × 恐るべきホープが、
Intel® Edisonというテクノロジーを使いこなすことで生まれた、先見性あふれるハート型デバイス。
こんなに小さなガジェットひとつで、人とモノとが会話する時代が訪れるとは、いったい誰が予測できただろう?
その記念すべき幕開けを導いた2人の才能。ぜひ、お見知りおきを……!

8月2日、MFT 2015内 Intel『Artist Showcase』会場にて

ARTIST PROFILE

BIRDMAN

最先端のデジタルプロモーションを手がける精鋭チーム。Webはもちろん、インタラクティブなインスタレーションや制作物、リアルイベントなどの制作に関わっている。ソフトでもハードウェアでも今まで誰も体験したことがないようなモノを作ることに常にチャレンジしている。Cannes Lionsシルバー、OneShowゴールド、広告電通賞デジタル部門グランプリ2回、Code Awardグランプリ、ADFESTゴールド, Spikes Asiaシルバーなど国内外にて120以上のアワードを受賞。

http://www.birdman.ne.jp