TikTok Effect Houseとは

TikTok Effect HouseはTikTokが公式で提供しているARエフェクトの制作ツールです。顔に重ねるフィルターや、表情に反応する演出といった、TikTokでよく見かけるエフェクトを自分で作って投稿できます。
利用はWindowsとMacのデスクトップアプリから行い、無料で使えます。専門的な3D開発の知識がなくても、用意されたパーツを組み合わせるだけでエフェクトを形にできるのが特徴です。
この記事では、Effect Houseの中でも動きをつけるための仕組みであるビジュアルプログラミングのさわりだけを扱います。
ビジュアルプログラミングとは
ビジュアルプログラミングは、文字でコードを書く代わりに、ノードと呼ばれる箱を線でつないで処理の流れを組み立てる方式です。コードで書く「いつ(イベント)」「何を覚えておくか(変数)」「どういう時に(条件分岐)」といった要素が、それぞれノードとして用意されています。
Effect Houseではこの仕組みをビジュアルスクリプティングと呼びます。同じ処理をTypeScriptのコードで書くこともできるので、この記事では両方のやり方を見ていきます。
題材として、口の開閉を検知してFilterをON・OFFするエフェクトを実装してみます。
触ってみる前の準備
まずは公式サイトからアプリを入手します。
公式サイトからダウンロードして起動します。以下の画像のような画面が表示されたら準備完了です。

ビジュアルスクリプティングで作る
最終的に、口の開閉を検知してFilterをかけるサンプルを作成します。完成イメージは以下の通りです。

Filterを用意する
まずは、上のアイコンが並んでいるメニューから「Filter」を選択します。すると以下の画面になります。右側にあるINSPECTORの「Texture」から、さまざまなエフェクトを選べます。今回は「Rainbow Wave」を使いました。

口の開閉を検知するノードを組む
ここからVISUAL SCRIPTINGタブを使います。パネルの空いている所で右クリックしてAdd nodeを押します。検索ボックスに mouth と入力し、「Mouth Open」系のノードを追加します。もう一度右クリックして set enabled(または set visible)を検索し、追加します。

配置が終わったら、ノードをつなげます。
- Begin: Exec → Set EnabledのEnabled OnをOnにしたノードのEnter
- End: Exec → Set EnabledのEnabled OnをOffにしたノードのEnter
- Set EnabledノードのComponent → Filterを指定

Componentを選択するとシーン選択ウィンドウが立ち上がります。ここでTransformではなくFilterを選択してから、Filterを選びます。
![]() | ![]() |
|---|---|
| ComponentのNoneの部分をクリックしてシーン選択ウィンドウを起動します。 | シーン選択ウィンドウ上部のセレクトからFilterを選択し、さらに階層構造になっているFilterを選択します。 |
これで、口を開いている間だけFilterが有効になるエフェクトができました。
TypeScriptで制御する
さきほどビジュアルスクリプティングで作ったFilterの切り替えは、TypeScriptのコードでも実現できます。ここではいきなり完成形を作らず、Hello, World!から段階を追って同じ動きにたどり着いてみます。まずはスクリプトを作成します。

スクリプトを開くとコードエディタが立ち上がります。

⌘+Sで保存され、プレビューに即時適用されます。コードを書きながら結果をすぐ確認できるので、動作を試しながら進められます。
Hello, World!
まずは動作確認も兼ねて、起動時にログを出してみます。
@component()
export class NewScriptComponent extends APJS.BasicScriptComponent {
/**
* Called before the first frame update
*/
onStart() {
console.log("Hello, World!🎉");
}
/**
* Called once per frame
*/
onUpdate(deltaTime: number) {
}
// Insert more for your logic
}
onStartはエフェクトの開始時に一度だけ呼ばれます。保存するとログに出力されます。

1秒ごとにログを出してみる
次に、毎フレーム呼ばれるonUpdateを使って、1秒ごとにログを出してみます。
@component()
export class NewScriptComponent extends APJS.BasicScriptComponent {
// 変数(数を覚えておく箱)
private elapsed: number = 0; // 経過時間の合計
private count: number = 0; // 数えた秒数
onStart() {
console.log("スタート!カウント開始します");
}
onUpdate(deltaTime: number) {
// deltaTime = 前のフレームからの経過秒数。毎回足していく
this.elapsed += deltaTime;
// 合計が1秒を超えたら…
if (this.elapsed >= 1) {
this.elapsed -= 1; // 1秒分を引く
this.count += 1; // カウントを1増やす
console.log(this.count + "秒経過");
}
}
}
onUpdateの引数deltaTimeには前のフレームからの経過秒数が入ります。これを足し続けて1秒を超えたタイミングでログを出すことで、1秒ごとのカウントを実現しています。

1秒ごとにFilterをON・OFFしてみる
最後に、ビジュアルスクリプティングで作ったFilterの切り替えをコードでも書いてみます。
@component()
export class NewScriptComponent extends APJS.BasicScriptComponent {
private targetFilter: APJS.SceneObject | null = null;
private elapsed: number = 0;
private isOn: boolean = true;
onStart() {
const filterSceneObj = this.getSceneObject().parent;
this.targetFilter = filterSceneObj;
}
onUpdate(deltaTime: number) {
if (!this.targetFilter) return;
this.elapsed += deltaTime;
if (this.elapsed >= 1) {
this.elapsed -= 1;
this.isOn = !this.isOn;
this.targetFilter.enabled = this.isOn;
console.log("フィルター: " + this.isOn);
}
}
}
ここではスクリプトをFilterの子オブジェクトに配置している前提です。getSceneObject().parentで親をたどり、対象となるFilterを取得しています。あとは1秒ごとにenabledを切り替えるだけで、ビジュアルスクリプティングのSet Enabledノードと同じことをコードで行っているイメージです。

Gifに変換したのでチラついて見えますが、正常に動作しています。
さわってみた所感
クリエイターが作ったサンプルを見ると、顔の一部が変化するものやゲームとして遊べるものなど、面白いエフェクトが数多く存在しています。
ビジュアルスクリプティングなら線をつなぐだけ、TypeScriptなら書き慣れた文法でロジックを書けるので、入り口のハードルは思ったより低いと感じました。
興味を持ったらまず1つ作ってみるのが、楽しみながら覚えるいちばんの近道だと思います。

