Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: CFR[]: Very minimal drawing language with 5 commands: C, F, R, [, ] (susam.net)
176 points by susam on Oct 21, 2023 | hide | past | favorite | 70 comments



This is awesome. I got nerd-sniped trying to create Moroccan Zellige [0], a highly geometric tiling, which seems like a great match to CFR[].

My best rendition (would have been better if there was a counter clockwise rotation, improvements to my code are welcome!):

[[FFFFFFR[[CFFFFF]]][[[[[[[FF]]]]RR[[F[CC[RC[FFFFFF]]RFFFFF[RFFFFF][R[CFFFFFF]]]]]R[[[[FFFFFFFFFFFFFFFFFFFFF]]]]][[FFFFFFFFF]]]]

[0]: https://zellige.info/


Just wanted to say that I think your rendition of Zellige is really nice, thank you for sharing! This is a very cool drawing language, and I think your art here is a really solid example of how creative you can be even with very limited tools.

I made a pinwheel, which is maybe not that exciting, but was a lot of fun for me. I like the bracket syntax a lot, and figuring out how to make this work actually recaptures some of the old sense of exploration and fun that I felt before programming was my day job.


Thanks, please consider sharing your pinwheel, I'm sure it was awesome to make too!

You're touching on an important point. I tried to figure out CFR[] by trial and error and it was a lot of fun, the kind of which I didn't feel for a long time. I think this boils down to two things: - Bounded complexity, which encourages you to explore it to its fullest - Immediate feedback, the result is not disconnected from the act of editing the code, so your iteration cycle is super short.

Congrats OP, amazing design work!


Another zellige motif [[[FFFFRFFFRRRRRR]]]


Nice one! If only we could combine the two :)



I added some letters to a demo and made https://susam.net/cfr.html#B15FRCFE7R2F4C11BR3E6C2E3. Thank you for making this! I don't know how it works but it is fun!


This is really neat!

I made a space-filling curve:

[[[[[[[[[[[[[[[[FR]]FRR]]RRRRRRF]]FFFFFFFRR]]RRRRRRFFFFFFFFF]]FFFFFFFFFFFFFFFFFFFFFFFRR]]RRRRRRFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF]]FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFRR]]

It's not very interesting by itself (it just fills the whole screen with white) but you get cool-looking visualisations of its fractal-like structure by putting "C" at different points of the opening brackets.


That’s cool. Also adding “R”s at various positions leads to interesting effects.

For example:

C[[[RC[[[RC[[[CR[[[C[[C[C[FCR]]FRR]]RRRCRRRF]]FFFRFFFFRR]]RRRRCRRFFFFFFFFF]]FFFRFFFFFFFFFFFFFFFFFFFFRR]]RRRRRRFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF]]FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFFFFFFFFFFCFFFFFFFFFFFFFFFFRR]]CC


Tried seeing how small of a program I could fill the screen with. Came up with this, but feels like there has to be a more elegant solution ^^;

[[[[[[[[[[[[[[[[F]]]]]]]]RRF[[R]R]]]]]]]]]


Naive micro-optimization of your program (namely, things like FF is 1 char shorter than [F]) got that down to 40 chars:

    [[[[[[[[[[[[[[[FF]]]]]]]RRF[RRR]]]]]]]]]
I then found a solution in 37 which uses a slightly different approach (namely, create a pattern that spans the full X and Y output space, then shift it by one step each iteration):

    [[[[[[[[[[[[[[[[FFF]]]]]R]]]F]]]]]]]]


Me too:

]


New command: ;D


[[[FFR]]]FFRFFRFFCFFFFFFFFFFFFFFFCCCCCCC[[[FFR]]][[[FFR]]]FFCFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCCRR[[[[[FFFFFF]]R]]]FFFFFFFFFFFFFFFFFFFRRCCCCCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCCRRRRRRFFRFFFRFFFFFFFFFFFFFFFFFRFFRFF



I played around with it but I don't have a clue what I'm doing. Managed to make this pretty geometric shape based on one of your examples though: https://susam.net/cfr.html#B15F15CRE6RF2BR2FE6CE3

I wonder what an AI could design with instructions for how to use this.


Transform non-picturesque programming languages into editable piet[0] picassos in dna version of hamming codes.

Poincarne / Hilbert ideas in reasonable/editable form, L-Systems[1][2][3]

[0] : http://esolangs.org/wiki/Piet

[1] : apl for postscript : http://en.wikipedia.org/wiki/L-system

[2] : https://fse.studenttheses.ub.rug.nl/14938/1/MSc_EducationMat...

[3] : https://www.sidefx.com/docs/houdini/nodes/sop/lsystem.html

[4] : https://mizzlrblog.wordpress.com/2016/06/17/l-system-compile...

[5] : nature of code : https://www.youtube.com/watch?v=f6ra024-ASY


Thank you for some interesting reading!


> I wonder what an AI could design with instructions for how to use this.

I tried asking chatGPT to use it, removing the colour instruction to reduce complexity. After clarifying what each instructions does and providing some documented examples, it could write syntactically valid programs, but they never drew the shape or pattern it explained they would. I think the language being repetitive strings of characters doesn't mesh well with predicting tokens.


Maybe try adding spaces between the letters and then mechanically remove those spaces when rendering.


[EDIT: Fixed] I know you didn't mean this but the browser history trapping on every keystroke felt kind of abusive on mobile. Thanks for the fix.


Yes, this was reported at https://news.ycombinator.com/item?id=37969444 and I got a fix out shortly afterward. Please hard-refresh the page (ctrl+F5, cmd+shift+r, etc.) and let me know if you still see this issue. Thank you for highlighting this problem!


Works!


Edit: Fixed Edit: Fixed


Looks great, congrats!

Feature request: dedicated command for “forward without color”. Perhaps “L”, as in “lifting the pen”?

I know black on black is treated the same, but I would like the separation of concerns.

I would even accept dropping “C” if this helped (effectively: reducing color palette to two, making “C” work like my proposed “L”).

So perhaps this feature request is about defining custom color palettes.

Edit - random doodling: https://susam.net/cfr.html#B12F2E3RB3CF2E3RE2RE3CB6F2E3R4ER2...



Some ideas I had after playing a bit more:

- If you raised the bit limit and added a "skip" option (ability the move cursor forward without drawing) you could potentially render simple 3d objects and run Doom by converting objects to commands

- You could implement a function that simplifies your existing inputs, so: FFFF becomes [[F]]


How is [[F]] simpler than FFFF?


It's not, it's just an example. As the string gets longer you can definitely shorten it.


Cool! I love the absolute minimalism, it forces some creativity. I don't know if it would violate the spirit of the language to add some kind of state or conditional to make it Turing-complete.

I made a grid:

    [[[[[[[[[[FFFF]RR[FFFF]RR[FFFF]RR[FFFF]C[RRR][FFFF][RRR][FFFF][RRR][FFFF]]]]][FFFF]C]]]]]


Some like to use the Back button to go to the previous page


Oops! I like to use the back button too. While implementing the distributable link feature, I overlooked that this is breaking the back button behaviour.

Thanks for pointing this out! I have pushed a fix for it just now. Please hard-refresh the page (ctrl+F5, cmd+shift+r, etc.) and let me know if you still see issues.


The GitHub link is helpful, it might be useful to include what each command does at the bottom of the page so users don’t have to go to the GitHub page to figure it out


It stores the program in the URL bar so you can send other people links to your drawing.


Sure, but it also added 50 entries to my browsing history. You can use the browser history API to only replace the state instead of pushing new states on.



To get to the bottom left: [RRR][[[[[[F]F]F]F]F]F]F[RRR][[[[[[F]F]F]F]F]F]FF

https://susam.net/cfr.html#BR3EB6FEFEFEFEFEFEFBR3EB6FEFEFEFE...

Prepend a C to get there without drawing lines on the way. Suffix with [RRRRRR]C to rotate upwards and get a visible color again.

Whenever I'm lost I paste [[[[CFCF]]]] at the end to find my cursor.


Love this. I'm in the process of creating a language myself, though mine is more for UI.

Are you parsing it yourself or are you using something like ANTLR or tree-sitter?


Not OP, but I don't see why one would need to parse this at all. You can just step through the characters one byte at a time and execute them, using only a stack memory for return addresses to implement the [] behavior. No syntax tree needed.


Thanks! Glad you like it. The sibling comment by majewsky is spot on. There is not much parsing going on. Every character is read one at a time and the turtle's state is changed. Indeed the "[" and "]" commands are implemented with a stack.

The entire implementation is a single HTML page written in plain HTML + JavaScript. The evaluation of the code happens here: <https://github.com/susam/cfr/blob/0.2.0/cfr.html#L204>.


A minimal language for UIs sounds interesting. Can you share any details?


Sure thing! It’s still in early development and doesn’t have an engine yet, but the syntax is about 80% scoped out:

https://matry.design


This is very neat, the minimalist approach has a nice aesthetic and some of the examples here are way beyond what I thought you could do with it.


This is super fun to play with. Adding instructions at different points in this pattern generates some very cool overlay effects.

[[[[[[FFCFFFFFFFFFCCCCCCC[[[FFR]]FFR]]]RRRRRCFFFFFFFFFFFFFFFFFFC[[[FFCFFFFFFFFFCCCCCCC[[[FFR]]FFR]]]CCCCCCRRRRRRCCCCCCCCCFFFFFFFFFFFFFFFFFFFFFCCCC[[[FFCFFFFFFFFFFFFFCCCCCCC[[[FFR]]FFR]]]CCFF]]


Is there a measure of something being "canvas complete" where you can construct "any bitmap" given it? Analogous to Turing complete but limited to drawing on a bitmap canvas.

This is so cool by the way! I love this! Thank you for making this!! :)


“lossless image format" maybe?


Ha! I was thinking more "a drawing language" like the one in the Show HN post. So cool!

I was wondering if there are other languages like these, and a measure analogous to Turing completeness, but for "canvas completeness" for drawing any bitmap on a canvas! :)


C[FRC[F[F]]FF]RC[FCC[FFC[]FR]]C[FRC[F[F]]FF]RC[FCC[FFC[]FR]]C[FRC[F[F]]FF]RC[FCC[FFC[]FR]]C[FRC[F[F]]FF]RC[FCC[FFC[]FR]]C[FRC[F[F]]FF]RC[FCC[FFC[]FR]]C[FRC[F[F]]FF]RC[FCC[FFC[]FR]]C[FRC[F[F]]FF]RC[FCC[FFC[]FR]]C[FRC[F[F]]FF]RC[FCC[FFC[]FR]]


What’s the minimal program that actually draws something? I’m just getting black so far.

Edit: ah, it’s F


Or ] if you want to make a red screen ;-)


May I suggest the name `turtlefu*` or `turtle[fu]`?

`CFR[]` is brilliant for being descriptive, and I think it’ll stick. But it read to me like a regulation or tax code. Perfect for misdirection.

No one ever suspects the return of LOGO as “Brackish Bletchly!”


This is so much simpler than APPLE //e LOGO. Or Etch A Sketch with color, undo, composition, and perfect alignment.

Okay, who's going to write the first interpreter in bf that's also a quine? ;D


Origami scarab

[[[[[[[[FFCFF]]RR]RR]R]RR]R]]


Used the five commands in the order presented, nothing happened. I guess this is a puzzle and not a demo. And on to the next thing I guess.


So it should be possible to combine this with piet and write a quine... Just putting this out there hoping I can need snipe anyone


CRRRRFFFFFFFFFFFFFFFFFFRRFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFRRCCCC[[[FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFR]]]


Very cool idea

I'd suggest a little hint box below explaining what do the commands do, I had to check github for those


The buttons have a tooltip with a short explanation.


Which are not visible on a smartphone, and IMHO should appear right away without having to hover anyway.


Thank you for sharing this feedback. I have updated the implementation and added a '?' button now that can be pressed on a smartphone to bring up a help screen. The help screen has brief descriptions about each command.


[[[[[[[[[[[[FCRFRFRFRFRFRFRFRF]]]R[[[[F]]]]]]]R]R]R]R]R]


Its the simple things sometimes

[[[[[[[CC[[FF]]]]R]R]R]]]


"Awk-Apl SOLIDs, a logistician's tty take on analytic geometry"(unpublished) resources that might be of interest:

Techinques in the unpublished report make use of some solid applied analytic geometry regessions (byte sized pixels[2][3]) using sources of integer sequence[1] for a different mechanical engineering take on what CFR[] does.

Although, the overall goal was to maximize font type useage[4][6] while minimizing IRQ/type ball interuptions[5]. aka mechanical/impressionist e-ink. Takes less effort to generate striking details with CFR[].

**

[1] : HN resource on integer number sequences : https://news.ycombinator.com/item?id=36038302

    a few noteable websites :

      The Cinco Library Encyclopedia of Numbers : https://www.youtube.com/watch?v=rVtHrgdcvZA

      On-Line Encyclopedia of Inter Sequences   : https://en.wikipedia.org/wiki/On-Line_Encyclopedia_of_Integer_Sequences

      offline/local dabase intdb : https://github.com/popey456963/intdb
***

[2] 40 yr lang; https://www.fosslife.org/awk-power-and-promise-40-year-old-l...

[3] gawk chapter "11.3.11 And Now for Something Completely Different" program : https://www.gnu.org/software/gawk/manual/gawk.pdf

[4] DR thesis commentary : https://computerhistory.org/blog/discovering-dennis-ritchies...

[5] HN (char)0 = 0; (https://news.ycombinator.com/item?id=37910983 )

[5] "selectric typewriter goes from trash can to linux" : https://hackaday.com/2023/07/25/selectric-typewriter-goes-fr...

[6] diy typewriter : https://hackaday.com/2023/04/11/ibm-selectric-typewriters-fi... No comicon sans cast type example provided.


The syntax definitions on github are 1. not on the site, and 2. not very good, so here's some better ones:

F (fill/forward): draw a pixel and move forward

C (color): change the color

R (rotate): rotate the direction of F by 45 degrees

[ ... ] (loop): anything inside this is executed twice


> The syntax definitions on github are ...

Are you referring to the short list at the top of the README on GitHub? That list does not provide "syntax definitions". That's just a quick introduction about what is about to come.

The commands are explained in detail in the section "Commands" later here: https://github.com/susam/cfr#commands

> F (fill/forward): draw a pixel and move forward

The command "F" does not draw a pixel and move forward. Instead "F" moves forward first and then draws a cell.

> [ ... ] (loop): anything inside this is executed twice

The control flow command "[" can be written without a corresponding "]". For example "[", "[F", "[[F", etc. are valid CFR[] inputs. Again, the section of the README I have linked to above defines what "[" and what "]" are.


Barely anyone is going to go to the github, especially outside of HN, and only some of those people are going to read past the first image, so it's important that you get the most important information out there immediately. I glanced at the extended explanation and decided it would be faster to figure it out by putting stuff in the website. The instant visual feedback means you don't need to tell users every nuance of every command, but you do need to pick the specific details that users can use to learn everything else.


I have updated the implementation to incorporate your feedback as much as possible while maintaining accuracy in the command descriptions. Now there is also a new help screen which can be invoked by typing '?'.

I am hoping the information in the new help screen will make the commands easier to understand. However, if you or anyone has better ideas about describing the commands, please feel free to send pull requests.

Thank you for playing with this tool and for offering your feedback! It has been very helpful in refining the command descriptions.


> For example "[", "[F", "[[F", etc. are valid CFR[] inputs.

Are those different from just "F"?

If not, even if those are technically valid syntax, op's explanation seems clearer. I was confused by the README definition too and came to op's conclusion after experimenting for a while.

If they are not equivalent to just "F" I still don't get the distinction so the README definition is not very good considering it confused at least both op and me.

(This is meant as constructive criticism, I'm having a lot of fun playing with this.)


Thanks for the feedback. The code examples "[F" and "[[F" both paint one cell but the code "[" produces no visual change, so in that sense there is a difference between them.

Between "[F" and "[[F", there is no visual difference. The command "[" is a control flow command that does not alter the state of the canvas or the invisible turtle. Instead it merely marks the current position in the code as the beginning of a block. The execution continues normally after "[" regardless of whether there is a corresponding closing "]" or not. However, if a corresponding "]" is found, then the execution jumps back to the corresponding "[" and executes the enclosed block once more. This produces the effect of a loop that executes twice. For most drawing purposes the code bounded by "[" and "]" indeed behaves like a loop that executes twice.

I have updated the README to make this clearer. However, I am open to improving the README further if there are better suggestions. Pull requests are welcome too.


[flagged]


> You don't get to be angry

I am not.

> when the readme isn't directly linked on the website

It is.




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

Search: