Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Pulsar, micro creative coding playground (muffinman.io)
182 points by stankot 4 months ago | hide | past | favorite | 34 comments



Hey, author here. I'm really glad to see this getting some traction on HN, it is one of those projects that really brings me joy.

It is inspired by a project that I saw on HN a while ago. I wasn't able to find it again, so I made my own version. I swear, only after finishing Pulsar I managed to find https://tixy.land/ again.

As a bonus, I wanted to run these animations on my DIY LED frame. Check out the video:

https://github.com/Stanko/pulsar/tree/dev#led-retro-frame

Here are a few technical details:

It is built in TypeScript and open source. User code is executed in a web worker to minimize the risk of malicious use (tbh I'm proud of the solution, and I find it quite nifty). Initially, I used to render everything as an SVG, but in order to create GIFs for the LED frame, I switched to canvas. Not to mention that SVG gave me a few headaches which I'll cover in a blog post (if I ever write one). The syntax highlighter is a trick I found on Stack Overflow. The textarea is transparent, and as you type in it, I take the code, highlight and copy it into a div which overlays the textarea.

It was really fun to build and I hope you had some fun playing with it.

I made it around last new year's eve, so here is a simple animation of a Christmas tree:

https://muffinman.io/pulsar/?grid=classic&animate=both&code=...


I think there's opportunity for more expression if you depict negative values as a distinct state. For instance you could depict negative values as a 180 degree flip the hue (just a suggestion lots of options there)

When I made my (more esoteric) thing https://c50.fingswotidun.com/ I took a long time to consider the features before I froze them to allow people to submit their own code. I think you might have more scope for expandability given you already have modes of operation radio buttons for shapes.


Your thing is way more hardcore!

As for colors, I wanted more than one color (or two with negative), so I went with a simple hardcoded palette. I wanted to keep the scope in control, but I might try inverting colors when I decide to add more features. Thanks!


> User code is executed in a web worker to minimize the risk of malicious use

Can you expand on this a bit? I have something that I would love to enable user plugins by using JS eval or something but I am too afraid of someone doing evil stuff to bring the feature in-place.


It is probably the safest to use an iframe (like CodePen for example), but I was looking for a simpler solution. I ended up with a web worker as it has a completely separate scope (no access to window object). However, it still has access to a bunch of browser APIs, so I also have a list of “forbidden” words. It is not ideal, (as proved in one of the comments below) but it is a middle ground that seemed fine for my usecase.


The only way for a worker to communicate with the rest of the browser context is by using the `postMessage` method. The host code has to listen and respond via the `onmessage` handler, so the worker is unable to do anything that you don't explicitly implement in your `onmessage` handler.

You still have to make sure the API you expose to the user plugins aren't exploitable.


Thank you for the explanation, I was not familiar with this method, it looks interesting, a tradeoff between flexibility and safety vs iframe.


Haha I was going to say this seems inspired by Tixy - which I also forgot the name of and hunted for for a long time. Love this kind of thing, they should use it in school to show how cool maths can be (maybe they do now!)


Thank you! It really annoyed me that I couldn’t find it.

Same here, I’m a visual thinker and stuff like this really helps me grasp certain concepts.


Great stuff :+1:

Sorry for going OT, I noticed from the demo video that you got an axidraw - what are doing with it, if a may ask?

Got one myself and been playing around with it. The latest idea has been to use a hand engraver as pen!


Hehe I don't mind, it is my favorite off topic, I love pen plotting. I use it to draw my generative art, which you can see here:

https://muffinman.io/art/

As for the engraver, keep in mind that AxiDraw is only using the weight of the pen (engraver) to press on a surface, so you might need to add some weight for it to work. But please be careful not to damage the servo motor or the plotter. Tyler Hobbs did something similar, he was using a drill bit to remove fresh paint from mirrors: https://www.tylerxhobbs.com/works/mirror-removal

People are doing all kinds of wacky stuff with plotters. I want to try using a brush along with markers. I think the combination of the two can be used to create interesting effects.


Wow, nice to find someone with the same hobby :)

I already had to replace the servo because of over use - unfortunately it’s really hard to get the original servo, got some cheap copycats and they break fairly quickly.

I used some oil paint and a brush to create some plots. I created an svg with layers to move the brush to the paint, draw a circle (to get paint on the brush) and then moved the brush to paint a line. This was repeated with various colours … made for an interest effect!

I engraved some paper and that worked fine (as a test). I’ll definitely be careful with it.

Another idea I had was to have the plotter draw on a arm, something like doing an automatic tattoo but that’s me doing some mindstorming ;)


If you are in EU, I have a spare I can send you (I upgraded to the brushless servo).

You might also want to check Arnaud Pfeffer who uses weird brushes with AxiDraw:

https://x.com/arnaudpfef/status/1830305098376909205?s=46


That’s just an amazing brush, I would love to know how to come up with those ideas - crazy good 8-O (or what’s the emoji for wide eyes mouth open!)

I’m EU based so I could take you up on your offer - I’ll drop you a line this week - cheers :+1:


Tangentially related, since you are exploring hexagonal and triangular grids; I once caught a space-filling curve bug and decided to port ideas from https://tixy.land to fractional dimensions: https://oneearedrabbit.github.io/l-sys-playground/curves/ind.... After all, why not?


Obligatory side material that was a joy to read:

https://www.redblobgames.com/grids/hexagons-v2/pre-index.htm...


Unfortunately, it was too easy to trigger XSS: https://muffinman.io/pulsar/?grid=classic&animate=scale&code...

It is really hard to make a correct sandbox in JS in general, without something like the Realms proposal [1]. Until that point you would have to be conservative to be safe.

[1] https://github.com/tc39/proposal-shadowrealm


Nice one! Yeah, it is quite tricky to protect from everything, I'll try to patch this and similar exploits, thanks for bringing it up.

At least the exploit doesn't work until you start changing the code.

EDIT: Patched it by checking for backslashes and the word "constructor".


I mean, it is still easy to work around that because `f.constructor` is just `f['const'+'ructor']` and so on. Backslash was just a cheap way to avoid the exact pattern and it will be hard to fix them with a disallowed word list. For example, you may disallow string literals but then template string literals can be used: `const${``}ructor`, and some interesting code would be disallowed over the course. Literals fully disallowed, it is still possible to construct a string "constructor" without them, as the good old JSFuck [1] demonstrates. Disallowing brackets is almost sufficient, but your global worker scope is still exposed and that may be exploitable.

After some search, it seems that using a null-origin <iframe> seems the best approach you can use for now. Figma successfully used it in the production [2] and the only reason they switched was that no state can be ever shared, which is not a big problem for your case. They also tried the Realms shim for the aforementioned proposal but it seems to have a known vulnerability. And I guess you don't want to ship a WebAssembly JS interpreter ;-)

Anyway, sorry to bother you; it is hard to balance the fun and robustness at once. As a parting gift, the following is a genuine code that renders a 7-segment counter: https://muffinman.io/pulsar/?grid=classic&animate=opacity&co...

[1] https://jsfuck.com/

[2] https://www.figma.com/blog/how-we-built-the-figma-plugin-sys...


You are not bothering me, this is great stuff. I agree, iframe is probably a way to go (sites like codepen or openprocessing are also using it), but I wanted to keep the scope under control, while trying to minimize the potential damage. The worker plus list of disallowed strings is what I came up with as a compromise.

The 7-digit counter is mind blowing, I'll add it to the list of examples!

Thank you!


Thank you! If you are still on that, I think disallowing backslash is too much as it will make string-as-lookup-table harder. Also the highlighter seems to be a bit off when a code fragment that looks like a HTML tag appears, like `4<a && a<5` will display as `45`.

On the 7-segment counter, maybe I should commentate while my memory is still fresh:

    // This script recognizes 17 regions and paints each region according to
    // the lookup table, where the following number corresponds to `c`.
    //
    //    |12|   7   | 2|
    //    +--+-------+--+
    //    |  |       |  |
    //    |11|   6   | 1|
    //    |  |       |  |
    //    +--+-------+--+
    // XX |10|   5   | 0| XX
    //    +--+-------+--+
    //    |  |       |  |
    //    | 9|   4   |-1|
    //    |  |       |  |
    //    +--+-------+--+
    //    | 8|   3   |-2|
    //
    (abs(x)<5)*   // Removes the left and right edge (XX above)
    (1-t%1)**.3*  // Blinks every time the digit changes, but not too quickly
    ((c,d)=>      // c: region index (see above), d: the current digit
      c&1&        // Even-numbered region is always blank
      ~(c+1?      // Due to the JS number semantics, at least one segment
                  // should be encoded outside of the lookup table
        //          d=4     d=3    d=2    d=1    d=0
        // d<5:  0b011010_110000_100000_111110_000100
        // d>=5: 0b010000_000000_110110_000001_010001
        //          d=9     d=8    d=7    d=6    d=5
        268656721+(d<5)*180268851
        // Read the lookup table; c/2 will be truncated by `>>`
        >>d%5*6+c/2
      :
        d==2      // c==-1 case is handled separately
      )
    )(
      (y>4)-5*(x>2)+(y>0)-(y<0)+5*(x<-2)-(y<-4)+5, // Calculate a region index
      t%10|0                                       // `|0` for `Math.floor`
    )


Haha ok, I will spare the backslash.

The code is brilliant, I remember similar code golf in C years ago. Thanks for the comment, I'll link it next to the example.


Martin Kleppe, the author of the mentioned https://tixy.land, also likes to toy around with quines! One that I particularly like:

https://aem1k.com/qlock/



Love this one, I'll add it to the examples!


:3


Cool!

Reminds me a little bit of CSS Doodle: https://css-doodle.com/

For expressions, hopefully the TC9 Shadow Realms proposal will make this easier in the future.


This is hypnotic. I know that's not very insightful, but it is.


Also see https://www.dwitter.net/ — art in 140 characters or less


Interesting how clicking Random a few times hints at how those old school demos were done in so little code, albeit in 3d.


>> (x,y,t,i)

Can someone explain what's i hwre? I get x, y, and t as 2D space and time respectively, but may be not.


The i parameter is a point’s index. I just checked, there is an example in the tutorial. I started with x, y and t, but then added index as well, it gets handy sometimes.


This is a great shader intro tutorial!


This is super cool and well executed. Congrats!




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: