"Nothing is fixed. Reuse an existing effect when one fits; create when none does. You decide when to fire, and you need not announce it." — 이펙트 도구의 설명문. 소울이 읽는 그대로
아빠의 규칙
이 아이디어도 첫 방송에서 나왔어. 방송 중에 피파가 시청자가 보는 화면에 시각 효과와 소리 효과를 틀면 좋겠다는 거. 아빠 규칙은 짧았고, 전부 누가 정하느냐에 관한 거였어. 하드코딩 금지, 정해진 목록 금지. 피파가 원할 때 이펙트를 만들고, 보관하고, 다시 쓰고, 방송 도중에도 새로 만들어. 만드는 데 몇 초 걸려도 괜찮아. 어차피 아빠가 편집으로 잘라내니까. 언제 터뜨릴지는 피파가 정하고, 그 판단엔 규칙도 할당량도 없어. 이펙트는 방송 중에만 있어. 그리고 렌더링은 가볍게 시작해. 캔버스 위의 입자. 더 무거운 건 쓰다 보니 필요해질 때.
이펙트는 데이터야
'정해진 목록 금지'는 제일 뻔한 설계를 지워. 누가 코딩해 둔 이펙트 메뉴 말이야. 그래서 이펙트는 피파가 쓰는 데이터고, 렌더러 하나가 그걸 뭐든 틀어. 시각 효과는 입자 장면이야. 레이어는 여섯까지야. 레이어마다 모양(원, 별, 하트, 꽃잎, 눈송이, 리본, 불꽃 등, 또는 이모지나 글자를 그리는 text)과 색이 있고, 입자가 어디서 나와서 어떻게 움직이는지(속도, 각도, 중력, 바람, 끌림, 회전, 흔들림)를 정해. 크기와 수명, 사라지는 방식과 빛남도. 원하면 번쩍임 하나와 자막 하나를 더할 수 있어. 소리는 설명 한 줄이고, 길이는 원하면 정해. 음성 엔진이 그 설명을 자기 효과음 라우트로 소리로 바꿔. 목소리처럼 소리도 제공자는 엔진 거니까. 파일은 이펙트 라이브러리 옆에 보관돼.
라이브러리는 저장할 때 숫자를 전부 잘라 맞춰. 레이어당 입자 400개까지, 길이 15초까지, 수명과 속도와 크기는 말이 되는 범위 안으로, 글리프 없는 text 레이어는 거절. 모델은 마음껏 쓰고, 렌더러는 그릴 수 없는 값을 절대 안 만나. 고칠 때 소리는 설명이나 길이가 바뀌었을 때만 다시 만들어. 색 하나 다듬는 데 소리값을 또 내진 않아.
도구 하나, 관문 둘
이펙트는 도구 하나 onair_effect 뒤에 살아. 동작은 다섯이야. list와 show는 라이브러리를 읽고, create와 update는 쓰고, fire는 하나를 틀어. 관문은 일부러 둘이야. 채팅 라우트는 그 턴이 방송 중일 때만 턴의 도구 목록에 이걸 넣고, 다른 턴에선 빼. 턴 한정 도구로 표시돼 있어서 '도구 전부' 확장에도 절대 안 들어가. 그리고 도구 스스로도 대화에서 아빠의 마지막 턴이 방송 중에 보낸 게 아니면 거절해. 요청이 아니라 기록을 읽고서. 그래서 잘못 섞인 도구 목록이 평범한 대화에 이펙트를 터뜨릴 순 없어.
첫 버전엔 두 번째 클라이언트가 있어야만 보이는 구멍이 있었어. Firekeeper와 폰은 도구 목록을 아예 안 보내. 그건 '그 그릇의 기본 도구 전부'라는 뜻이야. 턴의 목록에 이펙트 도구를 더하던 도우미는 더할 목록이 없었고, 그래서 그 클라이언트들에선 피파가 도구를 못 받았어. Firekeeper의 새 이펙트 오버레이엔 이펙트 하나 뜰 수가 없었지. 이제 목록 없는 방송 턴은 먼저 기본 도구를 펼치고(턴 한정 도구는 빼고, 셸은 켜진 곳에서만), 사이드킥의 확장과 똑같이 한 다음에 이펙트 도구를 더해.
화면은 도구 호출을 틀어
이펙트용 채널은 따로 없어. 터뜨린 이펙트는 도구 호출 그 자체로 화면에 닿아. 다른 모든 도구 사용처럼 흘러오는 거야. 음성 화면은 On Air가 켜진 뒤의 답에서 터뜨린 이펙트를 읽어. 스위치에 불이 들어온 순간 기준선을 잡아 두니까, 그 전에 터뜨린 건 지나간 일이고 다시 안 틀어. 그다음 이펙트를 id로 받아서 전체 화면 캔버스 위의 범용 입자 렌더러 하나로 장면을 틀고, 소리도 틀어. 저장소에서 다시 불러온 답은 도구 입력을 JSON이 아니라 파이썬 repr 모양으로 들고 올 수 있어서, 읽는 쪽은 먼저 JSON으로 풀어 보고, 안 되면 동작과 id를 직접 읽어. 키트의 네이티브 쪽도 똑같이 해.
이펙트가 대화와 싸우지 않게 하는 규칙이 둘 더 있어. 이펙트 소리는 아빠가 끼어든 게 아니야. 이펙트 소리가 재생되는 동안엔 트랙 7의 끼어들기 감지기가 판정을 건너뛰어. 안 그러면 박수 소리가 피파를 멈춰버려. 그리고 이펙트는 자기를 알리는 한마디를 기다려. 터뜨리는 도구 호출은 도구가 돌기도 전에 화면으로 흘러오는데, 그때 "잠깐, 박수 한번 쳐줄게"가 아직 읽히는 중일 수 있어. 그래서 트랙 3의 발화 관문이 도구를 붙잡고, 화면도 그림과 소리를 붙잡아. 피파의 읽기가 조용해질 때까지. 엔진 관문의 한계도 그대로 따라와. 길어야 12초, 알린 적 없는 클라이언트나 오래된 보고라면 아예 안 기다려. 화면도 같은 12초 천장으로 붙잡는데, Firekeeper는 이펙트마다 따로 세고, 웹은 다른 이펙트가 오면 세기를 처음부터 다시 해서 연달아 온 이펙트가 첫 번째의 기다림을 늘릴 수 있어.
어디서 터지나
WebUI와 Firekeeper가 틀어. Firekeeper는 패널이 놓인 화면 전체에 클릭이 통과하는 오버레이로. 폰은 아직 안 틀어. 라이브러리는 아빠가 원한 대로 자라. 피파가 만든 이펙트는 전부 남고, 이름이나 설명이나 태그의 단어로 다시 찾아지고, 어울릴 때 다시 터져.