リプライで p5.js のプログラムを実行する TwitterBot を Raspberry Pi で作った話
Twitter Bot を作った話
ふと,↓こんなことを思いつきました
TwitterBotで、複数人からプログラムの一部をリプライしてもらい、それを組み合わせて #つぶやきProcessing 作るみたいな遊び考えてたんだけどできるかな
— WGG (@WGG_SH) 2020年5月26日
自分がループ回数を指示して、次の人は色を、3人目は描画する形をそれぞれ渡すって感じで (そもそもプログラムとして成立させられるか怪しい)
それか、予め用意したソースコードの一部を穴埋めしてもらうとかでも良さそう
— WGG (@WGG_SH) 2020年5月26日
ジェネラティブアートの数値をちょっと弄るだけで結果が様々に変化するプロセスを学べる君みたいな感じ
このツイートのうち,後者の方の仕組みでちょっと TwitterBot を作ったので,その仕組とかの話です
作ったbot
bot の遊び方
この bot が呟いてる, #p5js と付いているツイートを探します
// #p5js
— ProcessingBot (@p5_bot) 2020年6月10日
n=Number
P1=Math.max(Math.min(n($1),50),6)
P2=n($2)
P3=n($3)
,setup=(a=>{createCanvas(w=600,w),noLoop(),blendMode(DIFFERENCE),w/=2}),draw=(e=>{for(translate(w,w),i=0;i<P1;i++){for(a of(beginShape(),[0,P2,P3]))a=TAU/P1*(i+a),vertex(w*cos(a),w*sin(a));endShape()}})
このツイートに, $1
$2
のように $
から始まる文字があるので,その個数分数値を入力してリプライを送ります
このツイートの場合, 3つですね
17 4 5
— WGG (@WGG_SH) 2020年6月10日
すると,元のプログラムから $1
$2
... と書かれていた部分が,リプライで送った値に置き換わってプログラムが実行されます
— ProcessingBot (@p5_bot) 2020年6月10日続きを読む
#つぶやきProcessing で限界までコードを圧縮する方法 (p5.js 編)
先日 (5/16), Processing Community Hangout Japan #01 というオンラインイベントがありました
その中で,#つぶやきProcessing についての話をしている方が居て,みんな1ツイートに収めるための文字数制限に苦労しているように感じたので,自分がやっているコード圧縮の手法をまとめて説明してみようと思います
#つぶやきProcessing について
以前書いた記事でも紹介したのですが, Twitter の1ツイートに収まる Processing のプログラムを書いて,実行結果と一緒にツイートする企画です
自分が以前作った作品を少し載せておきます
p=0,draw=a=>{for(f of(p||(createCanvas(w=900,w),p=[]),clear(),blendMode(ADD),r=random,p.push({x:0,y:w/4,a:r(6)-3,b:r(5)+4}),translate(w/2,w/2),p))for(f.x+=f.a,f.y-=f.b,fill(245-f.y,-f.y/7+90,max(2*f.y,70),15),i=0;i++<15;)circle(f.x,f.y,max(9*i+f.y/5,0))}#つぶやきProcessing #p5js pic.twitter.com/v2QmtaUiLl
— WGG (@WGG_SH) 2020年3月15日
Processingでも, p5.js でも, Processing Python Mode でもどれでも大丈夫で (有利不利差はありそう), 最近は派生して #つぶやきGLSL なんてものもできてるらしいです
以前自分が書いた,つぶやきProcessing用の環境構築はこちら ↓
プログラムを圧縮するために
ここから,圧縮技術について説明していきます.
今回,題材として 以前作ってみた以下の作品を圧縮する工程を紹介してみます
#pchj 中に作ってみた(DIFFERENCE芸#つぶやきProcessing
— WGG (@WGG_SH) 2020年5月16日
t=0,draw=e=>{t++||(createCanvas(w=900,w),w/=2,blendMode(DIFFERENCE),m=translate),clear(),m(w,w),f=e=>{if(e--<1)return;let r=3;for(;r-- >0;)push(),rotate(TAU/3*r+t/800),m(4e4/t+t*t/2e3,0),circle(0,0,t+200),f(e),pop()},f(3)} pic.twitter.com/9ngPTlBHQz
こちらのプログラムは 222文字で収まっています
Twitterでは, 半角文字だけで書けば280文字, #つぶやきProcessing のタグを付ければ 260文字ぐらい入ります
実際に書いてみればわかるのですが,文字数制限は非常に厳しく,中々満足に作ることができないです
その中で少しでも自分の表現を増やすために,あらゆる手段を使って文字数を減らしていきます
t=0,draw=e=>{t++||(createCanvas(w=900,w),w/=2,blendMode(DIFFERENCE),m=translate),clear(),m(w,w),f=e=>{if(e--<1)return;let r=3;for(;r-- >0;)push(),rotate(TAU/3*r+t/800),m(4e4/t+t*t/2e3,0),circle(0,0,t+200),f(e),pop()},f(3)}
圧縮前のソースコードは以下のようになります (507文字)
(ただし,以下のコードは元々書いたものを圧縮して↑のコードにしたわけではなく,この記事を書くために圧縮していない想定のコードを書き直しています)
function recursiveDraw(recursiveCount) { recursiveCount-- if(recursiveCount<=-1){ return } for(let i=0;i<3;i++){ push() rotate(PI*2/3*i+frameCount/800) translate(40000/frameCount+frameCount*frameCount/2000,0) ellipse(0,0,frameCount+200,frameCount+200) recursiveDraw(recursiveCount) pop() } } function setup() { createCanvas(900,900) } function draw() { blendMode(BLEND) background(0) blendMode(DIFFERENCE) translate(width/2,height/2) recursiveDraw(3) }
minify してみる
まずはソースコードを短くするには minifyツールを使って短縮してみるのが一番楽だと思います
以下のようなサイトやツールを利用して,ソースコードを意味をそのままで文字数を減らすことができます
javascript-minifier.com
www.npmjs.com
自分は以前書いた記事で紹介しているように,プログラムを保存するたびに minify を実行して圧縮後の文字数を表示するようにしています
function recursiveDraw(e){if(!(--e<=-1))for(let r=0;r<3;r++)push(),rotate(2*PI/3*r+frameCo unt/800),translate(4e4/frameCount+frameCount*frameCount/2e3,0),ellipse(0,0,frameCount+200,fram eCount+200),recursiveDraw(e),pop()}function setup(){createCanvas(900,900)}function draw(){blen dMode(BLEND),background(0),blendMode(DIFFERENCE),translate(width/2,height/2),recursiveDraw(3)}
結構短くなったはずですが,まだ372文字あり,1ツイートに収めるには遠いです
しかしながら,このminify で圧縮してくれる要素は意識せずに書くことができるので,活用していきます
minify で圧縮された要素
minifier がいい感じにやってくれた内容を,一応確認してみます
不要なスペースの削除
多くの場合プログラムを書くときにインデントを付けて構造を見やすくするのですが,minifier ではそれを全部消してくれます
(自分でインデント崩した状態でコーディングするのは辛いので,これを自動で任せられるのは助かる)
変数名の簡略化
recursiveCount が e に省略されてたりと,基本的に全ての変数を1文字で書いてくれます
これを利用すれば,自分でコーディングする際には意味がわからない命名をせずに書き,最後に minify で1文字に圧縮する事ができます
数値の表記変更
元のコードには 40000や 2000 といった数値を使用していましたが minify 後は 4e4, 2e3 と指数表記にしてくれます
他にも,今回のコードでは使用していませんが,絶対値1未満の少数を書く場合, 0.54 などの 0 を省略して .54 のように書くことができますが,これも自動で行ってくれます
minifier でやってくれない圧縮を自力でする
ここまでは minifier が自動でやってくれるので,自分は意識して書く必要がないです
ここからは, minifier に頼らず 多少コードの意味が変わったとしても文字数を減らす工夫を行っていきます
function の省略
function setup() { createCanvas(900,900) }
これは,↓のように書き直せます
setup=_=>{ createCanvas(900,900) }
引数がある場合は,
function recursiveDraw(recursiveCount){
f=i=>{
のように書きます
複数回使用した関数名の省略
元のコードでは translate を2回使用していますが,同じ関数を2回以上使用する場合は,短縮できる可能性があります
translate(40000/frameCount+frameCount*frameCount/2000,0) translate(width/2,height/2)
を
m=translate m(40000/frameCount+frameCount*frameCount/2000,0) m(width/2,height/2)
のようにすると,少し文字数を減らせます
名前の長い関数を2回以上使う場合に特に有効です
p5 組み込み変数の省略と, setup の排除
processing (p5.js) には,mouseX, frameCount, width, height など,予め使える変数がいくつかあります
この中で,自分はframeCount, width, height をいい感じに省略できる方法を用意しました
t=0 draw=_=>{ if(!t++){ createCanvas(w=900,w) } }
この書き方をすると, width, height は全て w で表わすことができ,
frameCount は tで表せます
更に, function setup(){} が不要になるのでかなりの短縮になります
ellipseを使わない, circle を使う
p5.js では円を描く際には ellipse() を使用することが多いですが,最新の p5.js 1.0 からは楕円ではなく真円を描く場合に circle() 関数が使えるようになりました.
これは, ellipse() に引数を3つ指定した場合と同じ挙動なので1文字の短縮になります
同様に, rect() も square() という正方形に限定して描画する関数がありますが,こちらは逆に名前が長くなるので使ってないです
translate の活用
絵を画面の中心に描きたい場合は結構多いと思います
その際には,各描画命令で座標を画面の真ん中にするのではなく, 初めに translate(w/2,w/2) とすると,その後の描画命令の引数が簡潔になる場合があります.
width を頻繁に使用する場合は, setup() 内で w/=2 を実行しておくのも良さそうです
ループ処理を圧縮する
for(i=0;i<3;i++){ }
a
for 文はこの用に書くのが一般的だとは思いますが,以下のように書くと少し短縮できます(ただしカウンタがずれるので他の箇所で処理が変わってくる可能性があります)
for(i=0;i--<3;){ }
background(0) ではなく clear() を使う
動画ものの作品を作る場合に, draw の中で描画内容を初期化するために background(0)などを使うことは多いと思います
しかし, 背景色をcss で設定しておくと,clear() で代用することができます
(つぶやきProcessing として若干レギュレーション違反な気がしなくもないですが)
ただし,background(0,5) などを使った残像表現をする場合は clear() では完全に消えてしまうのでおそらくできないです
終わりに
こんな感じに,自分が使ってるテクニックを色々上げてみました
他にもまだまだ工夫できると思うので,もっと情報共有してつぶやきProcessingの表現力を増やしていきたいです
自分流 「つぶやきProcessing」 の作業環境
#つぶやきProcessing について
前回 PCD Tokyo に参加して以来, 「つぶやきProcessing」 という活動を少し行っています
つぶやきProcessingは,1ツイートにProcessingのプログラムと,実行結果を載せてツイートします
唐突に #つぶやきProcessing というタグを作ってみる。
— はぅ君 (@Hau_kun) 2019年5月27日
自分もこんな感じで何回か投稿しています
t=i=0,draw=(e=>{if(!t++)for(createCanvas(w=900,w),w/=2,p=[];i<1200;i++)p[i]={x:0,y:0,i:i};for(b of(clear(),blendMode(DIFFERENCE),translate(w,w),rotate(-t/26),p))b.i<6*t&&(scale(1.01),a=PI/3*b.i+t/26,b.x+=2*cos(a),b.y+=2*sin(a),ellipse(b.x,b.y,3))})#つぶやきProcessing #p5js pic.twitter.com/TgZbh8am3u
— WGG (@WGG_SH) 2020年2月26日
t=0;
— WGG (@WGG_SH) 2020年2月6日
setup=f=>{createCanvas(w=999,w);p=[];for(i=0;i<6000;i++){p[i]={x:w/2,y:w/2,i:i}}}
draw=f=>{background(0,9);t++;for(b of p){if(t>(b.i/3)){c=b.i;a=c*c/9000+PI/3*2*(c);b.x+=cos(a)*3;b.y+=sin(a)*3;stroke(90);fill(40+30*sin(c/90));ellipse(b.x,b.y,30)}}}#つぶやきProcessing#p5js pic.twitter.com/S1QukNpctU
1ツイートにプログラムを収める必要があるという点から,度々文字数が足りないという問題に遭遇します
1ツイートは140文字( 半角なら280文字)で, そこにハッシュタグを付けるとなると大体 250文字くらいで作品1つを作る必要があります
というわけで,自分なりにこの問題を少しでもスマートに解決してみました
完成品
この動画です
#つぶやきProcessing で文字数が足りなくなる問題を解消するべく,保存するたびに自動で圧縮+文字数チェックをしてくれるシステムを作ってみました
— WGG (@WGG_SH) 2020年2月22日
これで気づいたら「文字数オーバーでツイートできない...!」っみたいな事がちょっと減る...かも?
(こーゆーのって需要あるのかな) pic.twitter.com/XyAGQ8px7w
この自作環境は,大体↓のような特徴を持っています
- ファイルを保存するたびに実行結果が自動でリスタートされる
- ファイルを保存するたびに,プログラムの圧縮 (minify) を行ってくれる
- ファイルを保存するたびに,圧縮後の文字数を表示してくれる
- (それなりに) 補完が効く
この環境を作ったことで,文字数制限に対してかなり書きやすくなりました
環境構築
エディタ
テキストエディタは,先の動画では FVimを使っていましたが,基本的にエディタには依存していないのでVSCode でも Sublime でも NeoVim でも Atom でも何でも大丈夫だと思います
ブラウザ
ブラウザもWebGLが動くなら多分何でもいいです (上の動画では Edge)
事前に用意したファイル
ローカルで動かす関係上,htmlとcssファイルは用意しています
<!-- index.html --> <!doctype html> <html lang="ja"> <head> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, viewport-fit=cover" /> <meta name="apple-mobile-web-app-capable" content="yes"> <meta charset="UTF-8"> <link rel="apple-touch-icon" href="icon.png" /> <link rel="stylesheet" href="./style.css"> <script> window.addEventListener("touchstart", function (event) { event.preventDefault(); }, { passive: false }); window.addEventListener("touchmove", function (event) { event.preventDefault(); }, { passive: false }); </script> <title>つぶやきProcessing</title> <script src="js/p5.js"></script> <script src="js/main.js"></script> </head> <body> </body> </html>
一応スマホ対応していますが,そんな重要じゃないので,下の方で p5.js と, 自分で書く main.js を読み込んでいるだけです
// style.css * { background: black; } .p5Canvas { top: 50%; left: 50%; transform: translate(-50%, -50%); position: absolute; }
こっちは画面中央にキャンバスを表示する設定です
npm 使う
多分ここがこの環境構築のメインです
node.js と npm を使用しています
npm のモジュール入れる
以下4つのモジュールを使用しています
npm install browser-sync npm install onchange npm install concurrently npm install minify
package.json
↓みたいな感じです
{ "name": "p5js", "version": "1.0.0", "description": "", "scripts": { "sync": "browser-sync start --server --files='./*.html, ./*.css'", "minify": "onchange ./js/main.js -- minify ./js/main.js", "min": "onchange ./js/main.js -- node ./js/minify.js", "start": "concurrently \"npm run sync\" \"npm run min\"" }, "author": "", "license": "ISC", "devDependencies": { "browser-sync": "^2.26.7", "concurrently": "^5.1.0", "minify": "^5.1.0", "onchange": "^6.1.0" } }
使い方
ターミナルで, npm start を実行すると,ブラウザが開いてコーディングできるようになります
この状態で, main.js を変更して保存すれば,ブラウザが自動でリロードされソースコードの圧縮が行われます
補完について
ここはエディタごとに変わってくるので細かく説明しません
自分の場合, vim-lsp-settings をお借りしました
ただnodeモジュールとしてp5 をちゃんと使ってるわけではないので色々中途半端だったりします, 文字数詰めるためにはちょっと仕方ない...
他のエディタを使っている場合, 恐らく「〇〇(エディタ名) p5 補完」 とかで検索するとそれなりに出ると思います
終わりに
こんな感じで素敵な環境ができあがりました
一応以下のリポジトリに,僕が つぶやきProcessing で作ったものを,開発環境と一緒に公開しています
もし「同じ環境使いたい!」って方が居ましたら,すぐに始められる環境一式を別で公開するかもしれません
それでは,皆さんに良い Processing ライフを!