Hydra.cheatsheet

Livecoding відеосинт у браузері · hydra.ojack.xyz · клік по коду = копія

source geometry color blend / modulate .out(o0)

Sources

початок ланцюга — генерують текстуру
osc(freq=60, sync=0.1, offset=0)
осцилятор: смуги; offset додає колір
osc(6, 0.05, 0.8).out()
noise(scale=10, offset=0.1)
Perlin-шум, плавна текстура
noise(3, 0.1).out()
voronoi(scale=5, speed=0.3, blending=0.3)
клітинна сітка Вороного
voronoi(5, 0.3, 0.3).out()
shape(sides=3, radius=0.3, smooth=0.01)
багатокутник; sides=100 ≈ коло
shape(4, 0.3, 0.05).out()
gradient(speed=0)
RGB-градієнт по екрану
gradient(0.3).out()
solid(r=0, g=0, b=0, a=1)
суцільний колір
solid(1, 0, 0.4, 1).out()
src(o0 / s0)
взяти буфер виводу або зовнішнє джерело
src(o0).out(o1)

Geometry

трансформація координат
rotate(angle=10, speed=0)
оберт; speed = анімація
osc(10).rotate(0.5, 0.1).out()
scale(amt=1.5, xMult=1, yMult=1)
масштаб; xMult/yMult — асиметрія
shape(4).scale(1.5, 0.5).out()
pixelate(x=20, y=20)
блочна піксельна сітка
noise(3).pixelate(20, 20).out()
kaleid(nSides=4)
калейдоскоп; малі n → геом. форми
osc(9,0.1,1).kaleid(6).out()
repeat(x=3, y=3, offX, offY)
тайлінг сітки; repeatX/repeatY окремо
shape(4,0.2).repeat(3,3).out()
scroll(x=.5, y=.5, spX=0, spY=0)
зсув; speed-аргументи = рух; scrollX/Y
osc(10).scrollX(0,0.1).out()

Color

робота з кольором / яскравістю
color(r=1, g=1, b=1, a=1)
множить канали на значення
osc(20).color(1,0.4,0.8).out()
hue(amount=0.4)
зсув відтінку
osc(30,0,1).hue(0.3).out()
saturate(amount=2)
насиченість
osc(10,0,1).saturate(3).out()
colorama(amount=0.005)
циклічне зміщення кольорів — психоделія
noise(2).colorama(0.1).out()
posterize(bins=3, gamma=0.6)
квантизація кольору в N рівнів
noise(3).posterize(3,0.6).out()
thresh(thr=0.5, tol=0.04)
бінаризація ч/б по порогу
noise(3).thresh(0.5,0.04).out()
invert(amount=1)
інверсія кольору
osc(10).invert(1).out()
contrast(amount=1.6)
контраст
noise(3).contrast(2).out()
brightness(amount=0.4)
яскравість (+/−)
osc(10).brightness(0.2).out()
luma(thr=0.5, tol=0.1)
маска по яскравості (прозорість)
osc(10,0,1).luma(0.5,0.1).out()
shift(r=.5, g, b, a)
зсув кожного каналу окремо
noise(2).shift(0.1,0.3,0.6).out()

Blend

комбінують КОЛІР двох джерел
add(tex, amount=1)
сума кольорів
osc(9).add(noise(3), 0.5).out()
mult(tex, amount=1)
множення (маскування світлом)
osc(9).mult(shape(4)).out()
blend(tex, amount=0.5)
лінійний мікс (як opacity)
osc(9).blend(noise(2),0.5).out()
diff(tex)
різниця кольорів — інверсні зони
osc(9).diff(osc(13)).out()
layer(tex)
накладання поверх з урахуванням alpha
osc(9).layer(shape(4).luma()).out()
mask(tex)
обрізає по формі другого джерела
osc(30).mask(shape(4,0.5)).out()

Modulate

колір одного джерела ВИКРИВЛЯЄ геометрію іншого
modulate(tex, amount=0.1)
базове зміщення x/y по червоному/зеленому
osc(4).modulate(noise(3),0.5).out()
modulateScale(tex, mult, off)
модуляція масштабу
voronoi(5).modulateScale(osc(4),0.5).out()
modulateRotate(tex, mult, off)
локальний оберт по джерелу
osc(10).modulateRotate(osc(3),1).out()
modulatePixelate(tex, mult, off)
змінна піксельність
noise(3).modulatePixelate(noise(2),20).out()
modulateKaleid(tex, nSides)
калейдоскоп, кермований джерелом
osc(9).modulateKaleid(osc(2),4).out()
modulateRepeat(tex, x, y, offX, offY)
модуляція тайлінгу; modulateScrollX/Y теж
shape(4).modulateRepeat(osc(3),3,3).out()

Output / буфери

o0–o3 · фідбек · рендер
У збірці Mark'а: Hydra тут має кросфейд-компоузер — код, який ти запускаєш, лягає на пару службових буферів, а на екран іде вже змікшований результат. Тому в прикладах пиши голий .out() без аргументу — тоді кросфейд між евалами працює. Явні .out(o1) / .out(o2) / .out(o3) цей ефект обходять — беріть їх лише тоді, коли справді потрібен named-буфер (напр. src(o0).diff(o1)). Виняток — фідбек на самого себе: .out(o0) у парі з src(o0) лишається робочим.
.out(o0)
вивести в буфер (за замовч. o0)
osc(10).out(o1)
render(o0)
показати буфер; render() — всі 4 одразу
render() // o0..o3
src(o0) — feedback
читати власний вивід → петля зворотного зв'язку
osc(40,0,1).modulate(o0,0.04).out(o0)

Audio-reactive

обʼєкт a · FFT-біни мікрофона
a.fft[bin]
амплітуда смуги (0..n); в arrow-функції
osc(10).rotate(()=>a.fft[0]*2).out()
a.setBins(n=4)
кількість частотних смуг
a.setBins(6)
a.setSmooth(0.8)
згладжування реакції 0..1
a.setSmooth(0.9)
a.setCutoff / a.setScale
нижній поріг гучності / верхня межа
a.setCutoff(4); a.setScale(8)
a.show() / a.hide()
показати індикатор спектра для калібрування
a.show()

External sources

камера · відео · зображення · екран
s0.initCam()
вебкамера → буфер s0
s0.initCam(); src(s0).out()
s0.initVideo("url")
відео-файл як джерело
s0.initVideo("video.mp4"); src(s0).out()
s0.initImage("url")
зображення як джерело
s0.initImage("img.jpg"); src(s0).out()
s0.initScreen()
захоплення екрана / вікна
s0.initScreen(); src(s0).out()

Відео з YouTube / Vimeo

коротка відповідь: initScreen()
Коротко. Згодувати Hydra посилання на YouTube чи Vimeo напряму не можна — ні через initVideo("https://youtube.com/watch?v=…"), ні через iframe-embed. Це не недоробка Hydra і не питання правильного прапорця: так влаштований браузер.

Робочий шлях — захоплення вкладки: відкрий відео в сусідній вкладці, у Hydra-панелі виконай s0.initScreen(), обери ту вкладку у вікні браузера — і вона стає живою текстурою.
// ось і вся відповідь — YouTube у Hydra
s0.initScreen() src(s0).out()
Чому не виходить напряму — три різні стіни:
1. initVideo() кладе URL у <video>-елемент. Сторінка youtube.com/watch — це HTML-документ, а не медіафайл; <video> його просто не програє.
2. Справжні потоки з googlevideo.com — підписані, живуть кілька хвилин, прив’язані до сесії і не віддають CORS-заголовків. Навіть якщо висмикнути такий URL, Hydra ставить crossOrigin = "anonymous", і текстура повернеться чорною.
3. iframe-embed теж не рятує: браузер забороняє читати пікселі cross-origin iframe. У WebGL-текстуру вони не потраплять ніколи — це вимога безпеки, а не баг.

Тобто обхідного шляху в браузері не існує. Не витрачай час на пошук «правильного» URL чи параметра — його нема. Або захоплення екрана, або локальний файл (нижче).

Шлях 1 — initScreen()

основний · живий · нічого не качати
// 1 — базове захоплення вкладки
s0.initScreen() src(s0).out()
// 2 — вписати в кадр і підтягнути колір
s0.initScreen() src(s0) .scale(1.1) .saturate(1.3) .contrast(1.2) .out()
// 3 — шумова деформація поверх відео
s0.initScreen() src(s0) .modulate(noise(3), 0.15) .out()
// 4 — бас із мікрофона гойдає картинку
a.setBins(4) s0.initScreen() src(s0) .modulate(noise(2), () => a.fft[0] * 0.4) .scale(() => 1 + a.fft[0] * 0.2) .out()
// 5 — відео мішається саме з собою
s0.initScreen() src(s0) .diff(src(s0).kaleid(6).rotate(() => time * 0.1)) .contrast(1.3) .out()
// 6 — відео як маска для генеративного шару
s0.initScreen() osc(20, 0.1, 1) .modulateKaleid(src(s0), 3) .mask(src(s0).luma(0.35, 0.1)) .out()
Що треба знати про захоплення:
· Потрібен secure context. Публічний інстанс уже на https. На LAN піднімай через ./flok-lan.sh --secure (Linux/macOS) або .\flok-lan.ps1 -Secure (Windows). По голому http:// на LAN-адресу браузер getDisplayMedia просто не дасть.
· Потрібен жест користувача. Діалог вибору вікна з’являється на кожен Ctrl+Enter і автоматизувати його не можна — це навмисно.
· Обирай вкладку, а не «весь екран». Так ти не захопиш сам редактор і не отримаєш нескінченне дзеркало (екран показує Hydra, яка показує екран…).
· Звук окремо. Hydra бере з захоплення лише відеотрек, тож звук вкладки йде в колонки сам по собі й у a.fft та Strudel не потрапляє. Хочеш реактивність — лови звук мікрофоном (a.setBins(…)) або керуй з боку Strudel.
· Аргумент initScreen(index) у браузері ігнорується — у вихідниках він позначений як актуальний лише для десктопних збірок. Вікно все одно обирається в діалозі.
· Головна перевага: це жива картинка. Можна перемотувати, ставити на паузу, міняти ролик посеред сету — завантажений файл так не вміє.

Шлях 2 — завантажити файл

flokvid · запасний варіант
Якщо потрібен саме файл (стабільний луп, робота без інтернету, точний фрагмент), на VPS є хелпер flokvid. Він тягне ролик через yt-dlp, перекодовує в H.264 + faststart (браузери тихо відмовляються текстурити частину VP9/AV1-файлів), кладе у /root/cdn/flok/media/ і друкує готовий Hydra-сніпет. Ця директорія віддається з Access-Control-Allow-Origin: * — без цього заголовка текстура була б чорною.
// синтаксис
flokvid <url> [name] [--start HH:MM:SS] [--dur SECONDS] [--height 720] flokvid --list flokvid --rm <name>
// 15 секунд з середини, 720p
flokvid https://example.com/clip.mp4 mycut \ --start 00:01:30 --dur 15 --height 720
// що воно друкує у відповідь
s0.initVideo("https://mandrock-files.duckdns.org/flok/media/mycut.mp4") src(s0).out()
Чесно про обмеження flokvid. VPS сидить на дата-центровій IP Hetzner, і відеохостинги це бачать.

· Vimeo зараз не працює взагалі. Помилка виглядає так:
ERROR: [vimeo] …: Failed to fetch macos OAuth token: HTTP Error 401: Unauthorized

· YouTube на момент написання працюєyt-dlp розв’язує JS-челендж через встановлений deno. Але це хитка рівновага: у логах видно Signature solving failed / n challenge solving failed, тож качається не найкращий формат, і YouTube будь-коли може почати віддавати
ERROR: … Sign in to confirm you're not a bot

Якщо побачив будь-яку з цих помилок — це не твій Hydra-код і не CORS. Варіанти: передати кукі (yt-dlp --cookies / --cookies-from-browser), або завантажити ролик на своїй машині (домашня IP таких перевірок не ловить) і залити результат у /root/cdn/flok/media/.

Для прямих посилань на медіафайли й для сайтів без бот-перевірок flokvid працює без застережень.
Практика по файлах: тримай кліпи короткими й зацикленими (5–20 с), до ~1280×720 — Hydra щокадру заливає кадр у GPU-текстуру, і 4K-вихідник просто з’їсть fps. initVideo() сама виставляє muted, loop, autoplay і викликає play(), тож ролик крутиться по колу без звуку — типово робити нічого не треба. Якщо стоїть на першому кадрі — дотягнись до самого елемента через s0.src (це і є той <video>). Увага: s0.srcnull, поки не спрацював loadeddata, тож роби це окремим евалом.
// штовхнути вручну (окремий Ctrl+Enter після завантаження)
s0.src.loop = true s0.src.muted = true s0.src.play()
// керування відтворенням через той самий елемент
s0.src.playbackRate = 0.5 // вповільнити s0.src.currentTime = 12 // перемотати на 12 с s0.src.pause() // стоп-кадр

Сусідні джерела

камера · картинка
// вебкамера (s0.initCam(1) — друга камера)
s0.initCam() src(s0).kaleid(4).out()
// статична картинка як текстура
s0.initImage("https://mandrock-files.duckdns.org/flok/media/img.jpg") src(s0).modulate(noise(2), 0.1).out()
initCam(), як і initScreen(), потребує secure context — тобто https або localhost. initImage() теж ставить crossOrigin = "anonymous", тож картинка мусить віддаватися з CORS — /flok/media/ для цього вже налаштована.
hydra.ojack.xyz → external sources →

Час / динаміка

значення, що змінюються щокадру
() => …
arrow-функція переобчислюється кожен кадр
osc(()=>10+Math.sin(time)*5).out()
time
глобальний лічильник часу в секундах
shape(4).rotate(()=>time*0.2).out()
[a, b, c]
масив = секвенція значень по bpm
osc([10,40,80]).out()
.fast() / .smooth() / .ease()
керування швидкістюінтерполяцією масиву
osc([10,40,80].fast(2).smooth()).out()
mouse.x / mouse.y
позиція курсора в пікселях
osc(()=>mouse.x*0.1).out()

Config

керування синтом
hush()
очистити всі буфери
hush()
speed = 1
глобальна швидкість часу; bpm = 30
speed = 2; bpm = 60
setResolution(w, h)
роздільність рендера
setResolution(1920,1080)
update = (dt) => {}
кастомний колбек кожного кадру
update = (dt)=>{ /* logic */ }

Готові патчі

встав і запусти
// калейдоскоп-осцилятор
osc(10,0.1,1.2) .rotate(0.5) .kaleid(4) .out()
// шумова деформація
osc(4,0.1,1.2) .modulate(noise(3), 0.4) .colorama(0.1) .out()
// фідбек-петля
osc(40,0,1) .modulate(o0, 0.04) .out(o0)
// audio-reactive Вороной
a.setBins(4) voronoi(5,0.3) .modulateScale(osc(4), ()=>a.fft[0]) .out()
// ч/б тайли, що обертаються
shape(4,0.3) .repeat(3,3) .rotate(()=>time*0.1) .thresh(0.5,0.04) .out()
// два буфери + diff
osc(10).out(o0) noise(3).rotate(1).out(o1) src(o0).diff(o1).out(o2) render()

Audio-reactive патчі

потрібен дозвіл на мікрофон · a.show() для калібрування
// kick → пульсація масштабу
a.setBins(4) shape(4,0.3) .scale(()=>1 + a.fft[0]*0.7) .out()
// бас → оберт калейдоскопа
a.setBins(4) osc(8,0.1,1) .kaleid(5) .rotate(()=>a.fft[0]*3) .out()
// спектр у канали RGB
a.setBins(3) osc(20,0.1) .color(()=>a.fft[0], ()=>a.fft[1], ()=>a.fft[2]) .out()
// хет → пікселізація
a.setBins(4) a.setSmooth(0.6) noise(2) .pixelate(()=>2+a.fft[3]*50, ()=>2+a.fft[3]*50) .out()
// поріг гучності → спалахи
a.setBins(4) a.setCutoff(2); a.setScale(6) voronoi(8,0.2) .brightness(()=>a.fft[0]-0.2) .thresh(0.4) .out()
// бас модулює фідбек
a.setBins(4) src(o0) .modulate(noise(2), ()=>a.fft[0]*0.1) .scale(1.01) .layer(osc(30,0.1,1).mask(shape(4,0.3))) .out(o0)

flok.cc · Hydra ↔ Strudel

спільний livecoding-джем візуал + звук
flok.cc — браузерний спільний редактор із панелей-таргетів (hydra, strudel, p5, sclang, mercury…), до 8 слотів. Таргет міняється у лівому верхньому куті слота; Command → Add Pane / Configure (таргети через кому, напр. strudel, hydra). Нічого ставити не треба.
// Hydra ВСЕРЕДИНІ Strudel — initHydra() зверху
await initHydra() osc(10, 0.1, 1.2).kaleid(4).out() $: s("bd*4, ~ cp").bank("RolandTR909")
// аудіо-реактив з мікрофона (detectAudio)
await initHydra({ detectAudio: true }) osc(30) .modulate(noise(3), ()=>a.fft[0]) .out()
// H() — патерн Strudel як вхід у Hydra (в такт)
await initHydra() osc(20) .rotate(H("<0 0.5 1>")) .out()
// feedStrudel — піаноролл/scope як відео-джерело s0
await initHydra({ feedStrudel: true }) src(s0).kaleid(4).modulate(noise(2)).out() $: note("c e g b").s("piano")
// flok: у hydra-панелі P() бере mini-патерн Strudel
// hydra-таргет у flok, синхронно з часом Strudel shape(4) .scale(() => P("<1 2 3>")) .out()
// p5.js теж працює в hydra-таргеті flok
// hydra-таргет p5.background(0) p5.fill(255) p5.circle(p5.width/2, p5.height/2, 200)
Нюанс fft у flok: через те, як налаштована analyze-нода, після того як Hydra почала брати a.fft, кожну Strudel-панель треба один раз перееваліти вручну (Ctrl+Enter) — інакше дані спектра не підхопляться.
strudel.cc/learn/hydra →

Гарячі клавіші

редактор hydra.ojack.xyz
Ctrl+Enterзапустити рядок
Ctrl+Shift+Enterвесь код
Alt+Enterзапустити блок
Ctrl+Shift+Hсховати/показати код
Ctrl+Shift+Fформатувати (Prettier)
Ctrl+Shift+Sскриншот у файл