How to start a local server:
npx serve --cors
!exolang "textual" "http://localhost:3000/exolang.js"
!exolang "textual" "https://textual.savamala.top/exolang.js"
Tell Estuary that you are using Textual by starting your code with this line:
##textual
Textual renders on a fullscreen 2D canvas. Performance varies significantly between browsers and it’s much better in chrome/chromium than in firefox.
Display a text in the middle of the screen with default settings:
text("Hello, world!").out();
text supports multi-line text:
text("Hello,\nTextual!").out();
text(`Hello,
Textual!`).out();
Size:
text("Hello, Textual!").size(200).out();
Position:
text("Textual").move(-.4, .5).out()
text("Textual").move(-.4, .5*Math.sin(time)).out()
Color:
RGB from 0 to 1:
text("Textual").color(.5, 0, .5).out()
RGBA:
text("Textual").color(.5, 0, .5, .4).out()
Feedback:
fb(0.98);
text("Textual").move(-.4, .5*Math.sin(time)).color(.5,0,.3).out()
Emojis:
text(":moon :sun :water :fire :heart :mercury").size(100).out()
Font family:
text('Textual').font('wingdings').out();
Rotation:
text("textual").spin(.2).move(.2,.2).out()
text("textual").spin(time/4%2).out()
In spin() 1 is PI radiants.
Stroke:
By default, text is filled. Use .stroke() to render it as an outline:
text('textual').stroke(3).size(200).color('blue').out()
text('textual').size(200).color('pink').out()
Shadow:
Adds a shadow to the text. Arguments follow Canvas’s setShadow order: shadow(x, y, blur, color):
text("textual").shadow().out() // 2, 2, 4, gray
text("textual").shadow(4, 4).out() // 4, 4, 4, gray
text("textual").shadow(4, 4, 8, "purple").out()
Shadows are expensive on the 2D canvas, especially with large fonts. Use them sparingly in live performances.
All oscillators take a frequency in Hz. To synchronize with the tempo, pass cps:
osc(1) // 1 Hz, free-running
osc(cps) // one cycle per beat
saw(cps * 2) // sawtooth, two cycles per beat
tri(cps) // triangle
sqr(cps / 2) // square
Oscillator phase is anchored to the current tempo reference, so changing the tempo (e.g. with tap tempo) resets the phase.
text('textual').move(0,osc(cps)).out()
text(":moon").spin(saw(0.5)).size(500).out()
text(":moon").spin(saw(-0.5)).size(500).out()
fb(.9)
In Estuary, use two cells, one for Punctual and one for Textual.
Force the creation of the Punctual canvas first, as it is opaque:
hline 0 0.001 >> add;
Load Textual by using any sentence:
##textual
text('Hello, Punctual!').out()
Now, you can redirect your text into a stream:
text('Hello, Punctual!').out('my-stream')
The text is now invisible. Get it from Punctual:
v << vid "stream://my-stream";
v >> add;
Add any Punctual effects and enjoy!
This method uses a very hacky approach, because Punctual doesn’t support streams right now, and it depends on loading Textual before Punctual accesses any video.