z = n => n--&&C(T/2+S*T/R+t*9)*C(T/4-z(n)*2)-r/6
for (c.width=192,R=48,i=8064; i--;)
x.fillRect(
S+96, T+54,
C(Math.atan2(S=i%97-R,T=i/97-R,r=(S*S+T*T)**.5)*9)*20-r&44&&
r>36?r<42?T/R:S/R:1,
r>36?r<R:z(3)/5
)
I'll try to give a rough overview of what's going on here.
The function z produces the puddle (ahem - "event horizon") through a technique I accidentally found from "orthogonal" raymarching. The accident being the value is supposed to be the distance of an incrementing ray for an SDF (signed distance function), but instead the distance is completely replaced on each recursion resulting in the reflected fluid appearance. I think this is due to the ray distance oscillating backwards and forwards in a way that is similar enough to neighboring rays to not appear noisy while giving the appearance of reflection. The exact functions used for the SDF are not so important so long as it incorporates itself.
fillRect is doing pretty much everything else, by painting each pixel. To achieve shading without fillStyle, the width and height arguments are varied between 0 and 1 to give grey scale shading due to subpixel anti-aliasing. Speculative edges are also achieved with negative values (0 to -1) which creates white gaps and fill adjacent pixels instead.
The chevrons are sampled from a radial fractal a*20-r&44 where 'a' is the angle of the current position wrapped in some periodic function (cosine), and 'r' is the radius. The bitwise AND operator is the key in generating these types of patterns (although OR and XOR also work very well producing other types of patterns), the constants are found through - a lot - of trial and error. In this case the result of the fractal is used as a "truthy" condition (i.e non zero) to toggle between the shaded sides (X/48 or Y/48) and 1 (the dark bits).
Some other hacky details: S and T are the X and Y pixel positions, these names are used so that they can be referenced before they have been assigned because these names have already been assigned unused functions by dwitter. This saves some characters by allowing me to define X and Y inside of atan2 while also passing them as arguments, at the cost of the fillRect X Y position being invalid for the first pixel and from the previous iteration on the rest.
I'm finding a recurring property of dweets that end up packing a lot in - is sharing significant portions of code for different purposes, in this case it's fairly straight forward but still a big win in terms of characters: S, T and r are only defined once and used for both the puddle and the ring. This type of size optimization always gives a lot more road than syntactic wrangling, which while important are only finishing touches. However they cannot be forced, ideas with enough shared code can only be found.
Mind-blowing work! Is there any kind of tools for "reverse engineering" or otherwise unrolling/prettifying dweets so I can study them better? It's really cool but I barely know where to begin.
I've never found prettifiers to help much. Often there isn't much in the way of indentation or newlines for a formatter to be able to objectively insert. I mainly rely on the following while both making my own dweets and unpacking other dweets (which can be just as much fun btw):
1. Use a text editor: Dweets can get very dense and non linear, syntax highlighting can help a lot when deconstructing a dweet (essentially manually prettifying). I also find variable highlighting invaluable for understanding a dweet (i.e where you select one and all of the others are clearly highlighted). Often the code will be arranged in confusing ways just to squeeze extra characters out, e.g putting things in the incrementor position of a for loop 'for(i=8;i--;x.fillRect(blah))', often you will want to rearrange it to make it more legible while you are understanding the code.
2. Use Dwitter: When making new dweets or figuring out the behavior of existing dweets. The code runs as soon as you lift each finger, this creates a feedback loop between your brain and the computer which is faster than any other interactive computing environment i've seen. It seems absurdly simple but it's very powerful and you will learn a lot. JS things.. all of those niggling assumptions that you don't have time or inclination to investigate when writing "proper" code will suddenly be easy and necessary and interesting to figure out. Math things.. I usually suck at math, but when you can play with the variables and functions and watch patterns instantly change on the screen math suddenly feels like the most intuitive thing in the world.
3. Your brain: The lovely thing about code this size is it can all fit in your head with ease... After marinading in your head for long enough eventually the code falls away and the concepts remain, at which point you can think about it, run it, and manipulate it with ease.
[edit]
Quite a number of dweets are packed using the unicode escape trick these days to get 194 effective characters (including this one). To quickly get the unpacked code just replace 'eval' with 'throw' and it will be printed bellow the dweet.
Finally there is a small but active discord group where you might find people willing to help with golfing (or ungolfing) and figuring things out etc: https://discord.gg/emHe6cP
It reminds me of Newton fractals, which basically compute a color for each pixel that depends on the sequence of points visited by the newton root finding algorithm.
This is basically the same thing: tracing the orbit of a function that's very chaotic but has enough local coherence to get a smooth image.
As someone who's written a lot of impressive dweets -- what do you recommend for someone wanting to start out with this type of coding? Just going to dwitter and trying to decipher other people's dweets as well as writing your own?
Pulling apart dweets is a good way to get started and learn new techniques. Whenever I see a dweet I don't yet understand, I can't resist immediately pulling it to pieces - because I know the ceiling of time required to understand it is very low. Remixing is also a nice concept built into Dwitter that tempts you into golfing other people's code further and creating interesting derivatives - both are good motivators to thoroughly understand all of the techniques packed into a dweet.
Once you get a bit more comfortable I would resist the urge to immediately publish, you will be surprised just how much work you can put into 140 characters. Don't publish until you feel like you can't remove another single character and have the best possible combination... then sleep on it, you will be surprised at the sudden revelations that happen and give you more road to play with... this will happen so long as you are not overly precious about anything - you can't have everything - making frequent sacrifices is what leads you to more interesting combinations.
After building up a stash of unfinished ideas, you will also sometimes notice some synergy between them and can combine them into something more impressive. In the same vein... making tiny code is as much about exploration as experimentation... I discovered this combination that looks like a Stargate - i.e the skill is in finding things, rather than forcing your will upon a canvas.
A few commenters have pointed out this is not 100% vanilla. I've described how minimal dwitter's shim is, but I'd like to demonstrate it: Paste the following into any browser console and it will run all by itself: