DWITTER—SON1K, the winning entry of JS1k 2017 is a social AudioVisual LIVE coding environment for the Twitter generation.
In simple terms, DWITTER—SON1K is a very small code editor dedicated to produce Audio-Visual demos where you have 140 characters to write the Audio update function, another 140 characters to write the Visual update function, and a couple of helper methods to access a canvas and get some basic Math functions. At any time when authoring a demo, you can copy the URL from the address bar of you browser to share on social medias. Also it comes with an example Audio-Visual demo.
Inspiration
As the name suggest, this project was inspired by Dwitter which provides the same features and more albeit the sound support. The harsh constraint of 140 characters really drives creativity. It fell like a natural fit to add Audio support and share the result with the JS1k community.
Helpers methods
Adapting the concept to JS1k, I renamed the variable exposing the canvas and its context to the ones available in JS1k i.e. c
and a
respectively, and I added N = Math.random
. The full list of helper methods and variables in DWITTER—SON1K is:
a = a 512x256 canvas
c = its 2D context
t = the time in seconds
S = Math.sin
C = Math.cos
T = Math.tan
N = Math.random
R = returns an rgba(...) string. Example call: R(140,140,140,.5)
How does it work ?
The goal was to create a social LIVE editor for AudioVisual demos that fits in 1024 bytes. The LIVE editor part itself was rather simple but it had to be able to share demos and come wit a decent sample demo to meet the requirements of JS1k
The render loop
On any input, the functions fa
and fv
are created using eval(/.../)
with the value of the ia
and iv
inputs and a fixed function signature ( stored in the $
variable ) which passes the time in second and most of the other helper methods as agurment with a default value.
The fa
method is called continuously by the audioprocess
event of a scriptprocessor
and fed with the audio timestamp to generate the Audio signal. Similarly the fv
method is called continuously by a requestAnimationFrame
and fed the time variable to generate the Visuals.
Handling broken code
Broken code happens a lot as a result of input
even firing as the author writes in the two input fields.
The simplest way to handle this was to wrap the body of the functions fa
and fv
in a try{}catch(e){}
block. This is not ideal but ensures that they are always valid functions.
Unfortunately the code can be evaluted as you type or edit a for()
loop and lead to an infinite loop. To my knowledge there is no compact way to break out of infinite loops. The only way is to instrument any for()
loops to impose a maximum iteration or running time but it is error prone without extracting the abstract syntax tree of the functions... and that alone is a whole challenge to do in 1024 bytes.
Shareable URL
On any input, the value of the ia
and iv
inputs are merged together and set as the fragment URL of the page. This allows the user to get the updated URL to copy and share on social networks.
On start, this process is done in reverse and falls back to a default string of code for the ia
and iv
inputs to gibe a default Audio-Visual demo in case the user opened DWITTER—SON1K from a blank URL.
Source code
Here is the uncompressed source code. Remember that is inlined in the shim of JS1k which gives a 2D canvas and leverage the browser playing field. As for the compression to fit in 1024 bytes, I used the undefeated RegPack.
// List of a arguments for the Audio and Visual update functions
$ = [
`t = time_in_seconds`,
`S = Math.sin`,
`C = Math.cos`,
`T = Math.tan`,
`N = Math.random`
];
// Markup and explainations
a.insertAdjacentHTML(`afterend`, `<pre><b><h1>DWITTER—SON1K\nAudio Visual LIVE coding environment for the Twitter generation\nShare the current URL 🐦</h1>AUDIO\n<input maxlength=140 id=ia size=140>\n\nVISUAL\n<input maxlength=140 id=iv size=140>\n\n${$.join('\n')}\na = A 512x256 canvas\nc = Its 2D context\nR = rgba-string method ex R(140,140,140,.5`);
// The R method to get rgba color strings
R = (r, g, b, a = 1) => `rgba(${r|0},${g|0},${b|0},${a})`;
// The default value of the Audio input
ia.value = atob(top.location.hash.slice(1)).split("\n")[0] || "return(N()*(q=Math.pow(1-t*2%1,8)+Math.pow(1-t/8%1,16)*8)+(t*(t&24|32)*[4,3,2,5,0,4,1,3][t*8&7]%1)) /32";
// The default value of the Visuals input
iv.value = atob(top.location.hash.slice(1)).split("\n")[1] || "for(d=f=300;d-=.5;c.fillRect(256+d*C(A),128+d*S(A),d/9+q*q,d/9))A=N()*6.3,c.fillStyle=R(f*S(A+t*3+S (A*f/d+t)),f*S(A*5-t*3),d)";
// oninput, and start, update the fa and fv methods, and location.hash to get a sharable URL
(oninput = t => {
fa = eval(`(${$})=>{try{${ia.value}}catch(e){}}`);
fv = eval(`(${$})=>{try{${iv.value}}catch(e){}}`);
top.location.hash = btoa(ia.value + "\n" + iv.value)
})($a = new AudioContext);
// Use a ScriptProcessor node for the Audio
// and requestAnimationFrame for the Visual
with($a.createScriptProcessor(4096, ($f = t => {
requestAnimationFrame($f, fv(t / 1000))
})($t = 0), 1))
connect($a.destination), onaudioprocess = t => {
for (t = t.outputBuffer.getChannelData(value = 0); value < 4096;)
t[value++] = fa($t += 1 / $a.sampleRate)
}
Holy moly, I won JS1k
If you know me, you probably heard about JS1k, the annual JavaScript competition run by kuvos since 2010, giving JavaScript developers a shim and 1024 bytes to do something cool. I love this competition and have entered most editions, always ranked in the Top 10. It feels good to finally get my JS1k badge of honor.
Don't worry this will not stop me from entering the next editions.
Feedback
I hope you appreciate this production. People seemed to like it. At least it was great fun for me to write and play with.
Don't hesitate to share your DWITTER—SON1K creations with me.
Other recent experiments
There are many experiments and projects like DWITTER SON1K to discover other here.
- FRONTFEST MOSCOW It was an honour to be invited to Fronfest Moscow 2017 with the little family to give my first workshop; implementing a Twin-stick shooter using ES6 and Canvas, and to continue my CODE🎙ART series of talks + live coding aiming to inspire new web developer artists. on November 18th, 2017
- VOLTRA VOLTRA: Grinding the Universe, a gritty JavaScript demo, winner of the 1024 bytes demo competition at the Assembly 2017. on August 6th, 2017
- BREATHING EARTH Another take on Nadieh Bremer mesmerizing Breathing Earth visualisation, running at 60fps on a 2D Canvas without libraries or frameworks. on June 26th, 2017
- CODING⯌ART AT RENDER CODING⯌ART at Render 2017 was part of my series of talks + live coding aiming to inspire new web developer artists. on March 31th, 2017
- CODING⬢ART AT SCRIPT'17 Script'17 was all kinds of awesome: Great organization, line up, venue, catering, diverse and welcoming. It was a pleasure to talk to you all and LIVE code something useless before the party. Looking forward to see useless things from you all. on January 27th, 2017
- JSPONGY Since Mentor^TBC released Spongy, an amazing 128b intro raymarching a Menger sponge, I wondered how far such effect could be size optimized in JavaScript while keeping complex camera path. The answer: 281 bytes. on October 23rd, 2009
- MARS LANDSCAPE Remember the amazing MARS 4Kb intro by Tim Clarke in 1993 ? Here comes a remake in 256b using JavaScript and Canvas on October 16th, 2008
- EQUALIZER A little intro for the famous Equalizer BBS made in 4kb for MSDOS on April 23rd, 1997
Let's talk
Don't be shy; get in touch by mail, twitter, github, linkedin or pouet if you have any questions, feedback, speaking, workshop or performance opportunity.