Faster-than-Light memes 2023-03-19T19:06:24+00:00 https://faster-than-light-memes.xyz/ Benjamin Schwerdtner https://faster-than-light-memes.xyz/scittle-prints-itself.html scittle-prints-itself 2022-09-26T23:59:59+00:00 scittle-prints-itself

:faster-than-light /memes

scittle-prints-itself

Description of the crafting of a website. Up here.

Introduction

I was wondering about what is cool for a newcomer to coding. I love lisp and I think one of the real joys in programming is interactive programming. So if I find some way of communicating this magic, that would be great.

After my binaural beats adventure I feel like Scittle is an amazing tool so I want to try it on something bigger. So I had this idea of a Scittle website that prints its source code, then you update the code and get an immediate effect.

I wanted this to be my website so maybe a new team member can also go into the code and hack around.

Problem statement

  1. print your source code
  2. ability to update code
  3. no code editor in the browser (input field…) I am thinking getting this approximately non-clunky would be hard. Especially balancing the parens. I decided to go with a drag and drop area where you slurp in a file into the browser.
  4. This is not a Quine because it receives the source code as input

Some reagent code

I do some StackOverflow-driven development for a drag-and-drop area.

I needed to figure out how ondragover Event and friends translate with hiccup. Luckily the legendary question answerer @p-himik helped me out in the #clojurescript slack channel. Turns out that when I add :on-drag-over in the attribute map it works.

(ns main
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]))

(defonce state (r/atom {:code-text ""}))

(defn drop-area []
  [:div#drop-area
   {:style {:margin-top "1rem"
            :height "10rem"
            :width "10rem"
            :background "Aquamarine"}
    :on-drag-enter
    (fn [event]
      (set! (.. (js/document.getElementById "drop-area") -style -background) "cyan"))
    :on-drag-exit
    (fn [event]
      (set! (.. (js/document.getElementById "drop-area") -style -background) "Aquamarine"))
    :on-drag-over
    (fn [event]
      (doto
          event
          .stopPropagation
         .preventDefault)
      (set! (.. event -dataTransfer -dropEffect) "copy"))
    :on-drop (fn [event]
      (doto
          event
          .stopPropagation
          .preventDefault)
      (let [file (->  (.. event -dataTransfer -files) first)]
        (->
         (.text file)
         (.then
          (fn [t] (swap! state assoc :code-text t)))))
      (set! (.. (js/document.getElementById "drop-area") -style -background) "Aquamarine"))}
   [:div
    {:style {:margin "1rem"
             :padding-top "2.5rem"}}
    "drop a file here"]])

(defn code-snippet []
  [:div
   {:style {:background "gainsboro"}}
   (:code-text @state)])

(defn my-component []
  [:div
   [drop-area]
   [code-snippet]])

(rdom/render [my-component] (.getElementById js/document "app"))

(comment
  (swap! state assoc :code-text "foi110"))

With this, I have a drop area for a file.

Wonderful thing: Updating state redraws the UI for us.

Evaluating (swap! state assoc :code-text "foi110") makes reagent 's magic take effect.

Fetch

I also want to show the default code on the first load. After some StackOverflow research, I determine fetch should work.

(->
 (js/fetch "main.cljs")
 (.then (fn [x] (.text x)))
 (.then (fn [x] (swap! state assoc :code-text x))))

Pretty print the code..?

At this point my website looks like this:

VU5LqMM.png

Figure 1: Halfway there. Sort of printing the source code now.

I want to do something where at least the white space is rendered.

After searching the web, I decide I need a <pre> tag to say it is preformatted. Also <code> sounds great.

(defn code-snippet []
  [:div
   {:style {:background "gainsboro"}}
   [:pre
    [:code
     (:code-text @state)]]])

Update the function, re-eval the rdom/render form. Boom I instantly look at my updated visuals. With cider, I can also call cider-eval-buffer, or cider-eval-file. I first had the background style inside the code tag, which did not have the look I wanted. I can hack on a piece of UI in isolation. Directly. Without any mental suspension time. It is great. It is how all coding should be.

Eval string

Now for the magic of updating the website with whatever you upload. First, I ask Borkdude on slack how to evaluate a string. -> You use load-string.

(load-string
 (prn-str '(js/console.log "hello")))

Says hello in the console.

I update my file drop handler with the side effect of loading the text:

(fn [t]
 (load-string l)
 (swap! state assoc :code-text t))

(Yes there is a syntax error in this snippet).

I add this to the bottom of the file to see if my code is evaluated:

(js/console.log "hello2")

Now loading silently fails when I upload my file, but the text updates.

So I evaluate this in isolation:

(load-string (@state :code-text))

Ah, I get an analyzer error about l not being defined or something.

I update my handler like this:

(try
 (load-string t)
 (swap! state assoc :code-text t)
 (catch js/Error _ (js/alert "That code does not work.")))

following Stew Halloway's example of binary error feedback. Either there is an error, or there is no error. Error messages are just bloat anyway.

console says:

hello2

Another piece in place, another hit of dopamine, wonderful coding moments.

Only fetch once

I update the fetch code like so:

(or
 (:code-text @state)
 (->
  (js/fetch "main.cljs")
  (.then (fn [x] (.text x)))
  (.then (fn [x] (swap! state assoc :code-text x)))))

I can do this because in Clojure everything is an expression and I can put expressions anywhere.

Make a big aquamarine rectangle into a small magenta rectangle

I want something on the eyes so I change the style of the drop area:

{:margin-top "1rem"
 :height "5rem"
 :width "5rem"
 :background "magenta"}

Drag and drop, and:

IUrSY7t.png

Figure 2: Visuals updated via dragging and dropping a source file.

In emacs: list-colors-display, nice. And drag and drop with dragon.

This contraption of course pales in comparison to having a REPL running. But the idea is that it is might useful to somebody that doesn't even know what a REPL is. And if you are a beginner and now you wonder what that REPL thing is. Here I try to make a list of how to get a dev setup.

Here is an idea I had and did not put into the initial version: Make an undo button. So that the user can go back in the history of the website

Codepen

An arguably more mature version of this is up on codepen.

A key difference is that my website prints its whole code. No machinery is hidden anywhere.

If I may say so I think this is cute.

Date: 2022-09-26 Mon 13:21

]]>
https://faster-than-light-memes.xyz/small-and-friendly-errors-with-cider.html Small and Friendly Errors with Cider 2023-02-05T23:59:59+00:00 Small and Friendly Errors with Cider

:faster-than-light /memes

Small and Friendly Errors with Cider

Growing by removing is growing stronger.

Less obtrusive Cider errors for a quicker repl-driven experience and practice. How I came to like error overlays.

1gxO1dN.jpg

Figure 1: A rose plant growing, the sun and speed of light. The idea of a computer.

The Quick-Eval

Repl-driven development is a philosophy and practice that focuses on using a REPL (Read-Eval-Print Loop) to interactively evaluate code snippets and see their results immediately.

The key is the whole language is always there and being able to make small experiments to the point where it meshes with your thinking.1

A big part of the practice of Repl-driven development is something I am calling the Quick-Eval flow for this blog post.

  1. Modify source code sexp
  2. eval
  3. Repeat

I want to optimize this practice. The speed of 1-2-3 can easily go below 5 seconds and everything is possible here.

  1. By honing lispy2 skills and juggling those sexps.
  2. By smoothing out the edges of cider-eval.

This aspect of Repl-driven development, the Quick-Eval involves using the mind, the fingers, and the editor. It's like playing an instrument or learning a motor skill like juggling - it requires dedication and repetition to master the movements. But the beauty of this practice lies in the ability to customize the tool to fit your thought process, unlocking the limitless potential for effectiveness. Knowledge of the editor becomes a key to a world of endless detail, where you can continuously optimize speed, the expressivity of intent and precision. The goal then becomes programming at the speed of thought, making structural editing a superpower of the Clojure programming language. And with the power to shape Emacs into what you desire, the possibilities are truly endless.

Speed cubers deconstruct the minutia for their finger tricks. This means being explicit and thoughtful about what movements to do during the performance.

This blog post is about an aspect of 2, smoothing out cider-eval, by eliminating error popups.

Let it flow

The first time I saw a Clojure stack trace, I thought my computer is broken.3

(Ariel Alexa)

Alex Miller mentioned4 recently that Clojure error messages are designed to not include the stack trace by default.

A lot of these tools will automatically print the stack trace and the Clojure REPL never automatically prints stack traces […] It prints you like a 2 line error message. And you can ask for the stack trace if you want additional information. We intentionally don't show you that because usually most of that information is not useful to you.

Nice, sounds like less heavy error messages are something to try out!

Cider already has a minimalistic error feedback available:

(setq-default cider-show-error-buffer nil)

With this setting, we get a little overlay right next to our cursor when evaluating with error.

Xk7Lbc1.png

Figure 2: Emacs cider eval error overlay displaying a runtime error message

We achieve a more smoothed-out Quick-Eval flow.

The consistent presentation, regardless of the presence of errors, creates a smoother and more streamlined workflow. This helps keep the focus on the code and eliminates interruptions in the thought process.

My mind can stay in one place, focused on the code, without a wall of something was wrong disrupting my flow.

Sometimes you want to look at a stack trace

Ok *cider-error*, we can always stay friends.

I do not advocate throwing away the stack traces, at all. With REPL-driven development and Emacs we have the power to move fast, and decide to inspect the stack trace should we decide the info would be useful.

*e exists for inspecting stack traces. Also *cider-error* is still at our side, we still faithfully render our stack trace there.

A quick and precise pop lets us go to the error buffer when we decide to do so:

(defun mm/pop-cider-error ()
  (interactive)
  (if-let
      ((cider-error
        (get-buffer "*cider-error*")))
      (pop-to-buffer cider-error)
    (message
     "no cider error buffer")))

(define-key cider-mode-map (kbd "C-c E") #'mm/pop-cider-error)

There is also a cider buffer selection dispatch command:

(define-key cider-mode-map (kbd "C-c X") #'cider-selector)

cider-selector into x to bring up the error buffer.

Let's not jump to conclusions

With less auto-jumping, we move slowly and deliberately and call upon our tools when we decide to do so. In practice, this can mean we jump around at insane speed, but we do so controlled.

(setq-default cider-auto-jump-to-error nil)

Currently, if you interactively eval5 and you have a compile error, Cider jumps to the top of the file. This is because we do not track a file position during the eval and Clojure only sees a piece of string, not the file.

So the most important command, cider-eval, that I want to optimize for Quick-Eval, had a rough edge and now I smoothed it out.

Errors should be friendly

Who invented that errors are red and angry?

(set-face-attribute
 'cider-error-overlay-face
 nil
 :background "DarkGreen"
 :foreground "white")

Now I have these green and peaceful boxes that don't make my heart rate go up.

Errors are the normal, correct behavior of my program, certainly in the context of developing. Sometimes I even eval just to check that there is an error. This is what repl-driven development gives us. And it should not feel like doing something wrong.

Spartan feedback for load-file

Currently, if you load a file and there is an error, there will be little feedback. The lack of an eval result in the echo area is signaling an error.

The extreme: Just say Did not work

In Debugging with the Scientific Method, Stuart Halloway mentioned what I would be calling Red light, Green light error feedback. The system only indicates if something worked or not, but not what specifically went wrong.

A trick in emacs if you want text invisible is you set the foreground to the same color as the background:

(set-face-attribute
 'cider-error-overlay-face
 nil
 :background "DarkGreen"
 :foreground "DarkGreen")

P7cn70e.png

Figure 3: Emacs cider eval error overlay displayed as a full green block.

I was still seeing the error in the echo area. So I did a quick (setf debug-on-message "nothing") and found cider--display-interactive-eval-result calls message.

The visibility of the message is controlled by cider-use-overlays.

(setq-default cider-use-overlays t)

Lol, now errors are just green blocks and I do not see the error message anywhere. If that is better than getting the error message I cannot say yet.

Footnotes:

2

Or the pareidit-like editor capability of your choice

5

What I call interactive eval is cider-eval-last-sexp etc. As opposed to cider-load-file.

Date: 2023-02-05 Sun 10:50

]]>
https://faster-than-light-memes.xyz/intro-to-clojure.html intro-to-clojure 2022-09-12T23:59:59+00:00 intro-to-clojure

:faster-than-light /memes

intro-to-clojure

Introduction

Clojure is a modern, functional lisp with a fiercely convinced community and production orientation.

Start

I highly recommend Maria.Cloud, you can just go there and start coding. This is great for anybody that does not know yet x

A dev environment

Get prepared for your journey as a pilot of an alien spaceship. As a beginner I suggest either

Vs Code

Easy to make work. A great editor. You will not need to be ashamed of your choice. calva for vscode

Emacs

Strong affinity to hacker culture and a program that is more than a tool, but eventually an extension to your hands, heart, and mind.

Here is a config file I tried to make for beginners: https://github.com/benjamin-asdf/simple-easy-emacs

A framework by clojurians that might be just right for you: https://github.com/corgi-emacs/corgi

There is also doom and spacemacs that enjoy wide appraisal for their "out of the box" experience.

Nr 1 book for beginners

Slack

Meet nice people that are excited about the tech on slack. Seriously a lot of people hang there and answer beginner questions, everybody is welcome.

Other such lists

Carin is cool and has a funny picture of the emacs learning curve here: https://howistart.org/posts/clojure/1/

I like that they have a philosophy section mentioning Rich Hickey's talks this matters a lot to clojurians.

https://thoughtbot.com/blog/tips-for-clojure-beginners https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f https://www.clojurenewbieguide.com/

Date: 2022-09-12 Mon 20:23

]]>
https://faster-than-light-memes.xyz/dir-env-process-environment.html dir-env-process-environment 2022-10-17T23:59:59+00:00 dir-env-process-environment

:faster-than-light /memes

dir-env-process-environment

Introduction

Making shell commands is one of the great joys of emacs.

An advanced use case is that you want to set up a process environment for everything you do inside some project.

What I show here works for compile, shell-command, and even cider connections.

The setup

.dir-locals.el:

((nil
  .
  ((eval progn
         (make-local-variable 'process-environment)
         (setq process-environment
               (copy-sequence process-environment))
         (setenv "FOO" "hurr")))))

Yes, it is a bit convoluted but for a reason. We need to copy the list, else if somebody modifies the cdr locally, that would modify the global value. (In the case that FOO is an element in the list somewhere already).

The answer mentions setq-local. I tried that without success.

Shell-command and friends will inherit the process environmen. It pretty much just works.

From the project I currently work on:

((nil
  .
  ((eval progn
         (make-local-variable 'process-environment)
         (setq process-environment
               (copy-sequence process-environment))
         (setenv "AWS_PROFILE" "datalake-deployer")
         (setq-local cider-clojure-cli-aliases (concat cider-clojure-cli-aliases ":dev"))))))

Now I start cider connections with AWS_PROFILE set correctly. Also I have a babashka task for deploying that needs this env and it all just works.

Non-file-buffer

By default, .dir-locals.el does not apply to buffers without file names. (When the local variable buffer-file-name is not set).

There is a function hack-dir-local-variables-non-file-buffer for this purpose. This is called by dired, also magit.

I usually start processes from either a (source) file buffer or dired.

revert with grace

When you make a change to your .dir-locals, call revert-buffer in whatever buffer you want to have the effect. It hacks the local variables again for the buffer.

Date: 2022-10-17 Mon 09:56

]]>
https://faster-than-light-memes.xyz/pearls-of-introspection.html pearls-of-introspection 2022-11-12T23:59:59+00:00 pearls-of-introspection

:faster-than-light /memes

pearls-of-introspection

Recently, I watched BugsWriter falling in love with elisp 1 - he said about describe-mode

Why did nobody ever tell me about this?

This post is called Pearls of introspection.

All of Emacs is one bright pearl.

The describes

describe-mode

Lists all current keybinds in a buffer. Huge output, sometimes what you want.

describe-char

Emacs is a text editor, text is made up of chars. describe-char is useful if you want to know what face is rendering. It goes beyond that by showing string properties, overlays, and buttons. This can be useful for debugging.

Story: Once I deleted some packages on my system, then I go into emacs info and some of the text is not rendered right.

describe-char gave me the hint, the fixed-pitch face wanted "Monospace Serif" or some such but I only had Monospace.

So I did this and was happy ever after:

(set-face-attribute 'fixed-pitch nil  :family "Monospace")
(set-face-attribute 'fixed-pitch-serif nil  :family "Monospace")

Worth binding:

(define-key help-map (kbd "c") #'describe-char)

describe-key

"What does this key do?" - the answer is describe-key and hit the key.

describe-function

Emacs functions have amazing docstrings.

The help buffer has a keymap of its own, try ? to list keybinds.

Highlight: s to go to source.

describe-keymap

Probably faster than describe-mode, renders a keymap in a help buffer. Try describe-keymap -> help-map hehe.

prefix-help-command

You type ? after starting to type a prefix, then you get the binds.

embark-bindings

An alternative to the default ?, it is amazing. I get a vertico completing-read of the binds.

M-x describe-

There are more, some of them I never used. Emacs is vast like the ocean.

view-lossage

You accidentally hit some key, and something unexpected happens. view-lossage is your friend. It shows the last keys + commands.

It is like the morning-after pill version of describe-key.

story: I sometimes accidentally hit y before typing the text for an avy command.

Every time I was like "Holy cow whatever I hit there is powerful". 2

I didn't know about view-lossage. The thing is, it would not have helped, because avy is reading the key, then dispatching on the key. I guess it needs to go through the keymap system or something to show up in view-lossage.

apropos-value

This helps me about once per year.

It is extremely satisfying when you use this and it helps you. I once figured out about revert-buffer-function by using it.

You can revert shell-command buffers to rerun the command but I did not know by which principle.

apropos-value, then typing my shell command showed me there was a buffer-local variable called revert-buffer-function. It worked because Elisp closure objects print with their environment. In other words, the value of revert-buffer-function was a lambda that printed with my shell command. It is quite wild.

find-library

Read code, and do little experiments. I am not sure but I think there might be something profound about Elisp and emacs. I think the language is the program and the program is the language or something. Knowing the program is to know the language and knowing the language is to know the program. (Insert link to top Elisp functions blog post here).

where-is

I just discovered this via describe-keymap into help-map lol. This is like describe-key but you say the command and it says the keybinding.

Footnotes:

1

I thought lisp was going to be complicated but it is simple.

That is an interesting tension between outside perception and the actual language. Since you write the AST directly, you can say what you mean. The power of lisp is that the code is made of

  1. Ideas
  2. There is nothing else.
2

It makes the cursor stay and yank the target word. Hit ? before typing the keys for avy. The way it works is that avy can dispatch with different actions, the default is to jump to the location, but y for example yanks a word and makes your cursor stay where you are. It is really powerful. Ace-window has the same paradigm.

Date: 2022-11-12 Sat 16:02

]]>
https://faster-than-light-memes.xyz/the-joy-of-clojure.html the-joy-of-clojure 2023-01-26T23:59:59+00:00 the-joy-of-clojure

:faster-than-light /memes

the-joy-of-clojure

Introduction

We don't yet have a strong psychology and sociology of programming languages. One day we will, and we will be able to retroactively make sense of why Clojure is so powerful.

6usJE0L.jpg

Figure 1: The soul! A computer. Solar punk Clojure. The inside of a computer. Rubics cube colored gems are flying around. Lighting.

Paul Graham has suggested The Taste Test for understanding the power of a language. A programming language is a tool for thinking. Ultimately, its power comes from letting you think thoughts and express them. With sensibility (hypersensitivity), you can tell which language feels more restrictive than another.

If all languages would be the same, we would be programming assembly. But we are not, because we live in a world where languages have varying degrees of power.

Here I try to describe a few things that set Clojure apart, even from other Lisps.

What interactivity brings

With Lisp, you can quickly test each piece of code as you write it, ensuring that it is solid and functioning correctly before moving on to the next piece. This allows for a more streamlined development process and eliminates the need to keep in mind all of the potential issues that could arise with a piece of code. Additionally, the ability to quickly evaluate code and make small experiments allows for a more agile and flexible development process.

Here is a simple example:

(file-exists-p "~/.clojure/")

Are you getting the file path right? When I write Lisp, I know the answer while I am writing the program. I just eval whatever form I have to build the file path. Because of structural editing, I can wrap whatever form in (file-exists-p) quickly and experiment right then and there.

Doing little experiments is quick enough so it can mesh with your thinking. I have the freedom and agility to just do and move. We get the same power in the small during development and in the large while diagnosing a problem in production. 1

Philosophy of simplicity

Clojure first clicked for me via Rich Hickey talks. Keep things simple. Let data be data. Then you can just manipulate the essence of the stuff and be done. 2 , 3

He establishes the reasons for Clojure and the opinions on how to build things. And this permeates the culture.

The combination of data-orientedness and functional programming while making real-world pragmatic tradeoffs, lends itself to a complete4, robust, no-bullshit style.

Strong community

When I use a library I can go to the slack channel and usually the maintainers help me out. It happened to me more than once when I made a bug report, somebody fixed it within a few days. Everybody is excited about technology and wants to help you out. It ties in with the joy of Clojure as well. You get happy, friendly people. This is power. Humans are more effective when they are happy and friendly.

Not to mention Borkdude.

Non-breaking versioning

It is an explicit value (again from Rich Hickey's philosophy), to not make breaking changes. This is one of the things where once you have it, boy, you don't want to ever go back.

After acquiring this taste, getting broken because I bump a version makes me think Why are you doing this to me?.

The aesthetics of data orientedness

The core library is a huge lever because we lift everything that can be into the space of data structures, then manipulate those data structures.

Config input, REST API response, database queries, and responses. It is all the same kind of stuff once it is in our system.

I think of this as a lift and manipulate kind of lever. Unix has this with program input output text. Once you are in the realm of textual input and output you can leverage shell commands to manipulate. Emacs has the same thing with text in buffers. If you can express your domain as text in the buffer you can give it to the user for manipulation, and you can leverage the core library for manipulating text in buffers.

This data-orientedness is something I can expect from libraries and other people's code. The dominating amount of stuff will always be just data structures acting on pure functions.

Freedom and joy

The workflow is an utter joy, with the REPL at the center of it. I start a program once and it grows together with my ideas. There is something about a program that runs for days and you don't restart it that I cannot explain but it does something to your mind. The closest analogy is a kind of friendship with the program.

  • inline def some of your data, craft your code forms.
  • Functional programming (and The Value of Values)
    • able to fabricate data
    • able to get the time right with atomic successions
    • able to easily keep records of the past

I get a real kind of safety. The kind where I can sleep at night because I know my program is bug-free. You get this from there being few enough ideas in the program that you can hold the relevant pieces in your head.

Any program that is small enough to be self-evident is with a step function dramatically more likely to work bug-free. 5

It helps that everything is accessible from emacs which is the other ancient and powerful tool that feels like an extension of my mind.

Because of this focus, solidity and ease of development, I am more joyful and creative when programming with Clojure.

It allows me to focus on the actual problems. So I can spend time on getting the user experience right etc.

Summary

So here is a tool that lets me think the right thoughts and express them. It lets me move with ease where I want to be.

From Lisp, I get the interactivity and the tradition of FP and simplicity. When I want to make a small experiment with the code, I can just do a small experiment with the code.

Beyond that, there is a solid and dependable community, the data-orientedness and the leverage of targeting big platforms.

I can model information systems and can make the program just do what it needs to do, and do nothing more.

Clojure just delivers.

The silent competence of the computer.

(Marvin Minsky)

Note

I know there is a book of the same title. This is not accidental. The power of Clojure also lies in the joy it brings. An alternative title for this post could be Joy is power in analogy to Paul Grahams Succinctness is power.

Footnotes:

1

Bradford Cross has mentioned this and other things in a very convincing talk recently.

2

I believe I see hints of the same craftsmanship of getting shit done ideas in other places: Brian Will Object-Oriented Programmin Embarrassing, his counterexamples are very straightforward and complete. Chris Keaathley - SOLID code isn't flexible

3

Rich Hickeys greatest hits The start point. Get your mind expanded by simplicity and clear thinking.

4

Complete in the sense that you make a piece of code that gets the job done. Instead of scattering logic around the place.

5

Stuart Halloway has similar thoughts, Radical Simplicity. Other people like Dan Friedman also made this relatively explicit. I think at this point it is obvious that simplicity is crucial for building good software. The question is what is simplicity and how to do it?.

Date: 2023-01-26 Thu 09:48

]]>
https://faster-than-light-memes.xyz/binaural-beats-using-scittle.html binaural-beats-using-scittle 2022-09-19T23:59:59+00:00 binaural-beats-using-scittle

:faster-than-light /memes

binaural-beats-using-scittle

Introduction

The best thing about being a rationalist is figuring out you are wrong about something.

I was thinking that binaural beats belong together with astral travel, frankly, just down the toilet. Turns out that not only was I wrong, but binaural beats also have cool neuroscience and pose a fun engineering problem on top of that!

Here is the current result of my journeys up on gh-pages.

What are binaural beats?

Wikipedia

A binaural beat is an auditory illusion

For example, if a 530 Hz pure tone is presented to a subject's right ear, while a 520 Hz pure tone is presented to the subject's left ear, the listener will perceive the illusion of a third tone. The third sound is called a binaural beat, and in this example would have a perceived pitch correlating to a frequency of 10 Hz, that being the difference between the 530 Hz and 520 Hz pure tones presented to each ear.

Science

There has developed a growing science about improvements in memory and attention tasks when listening to binaural beats.

BTW the frequency and the time of the exposure matter (i.e. before and during the task is best).

Oh yes, I certainly want more of that memory and attention.

eHaaoYF.png

Figure 1: A brain with headphones flying at the speed of light through space. Lightning on the brain. There is a sine wave in the background. Stylized, Colorful, Cinematic, Digital Art.

Frequency

Theta waves (5Hz) actually decrease memory task performance. So I really do not want to listen to those. I heard Andrew Huberman saying that 40Hz is the most powerful.

If you know about brain waves, you might remember 40Hz as the legendarily intriguing gamma wave.

From wikipedia article:

Gamma rhythms are correlated with large scale brain network activity and cognitive phenomena such as working memory, attention, and perceptual grouping, and can be increased in amplitude via meditation or neurostimulation.

We are really talking about cool stuff here, attention, working memory, and memory processes. Also, the Binding problem.

The neuroscience of the auditory system

Hearing is cool because the neuronal neural information first goes into the brainstem and then goes up some nuclei up to the thalamus. And eventually to the temporal lobes of the cortex (the 2 arm things on the side of the brain).

This is different from the vision system where the info goes directly to the thalamus.

Quoting this paper:

Presenting two tones with a slight frequency mismatch to each ear separately creates a perception of a third tone, a binaural beat, that oscillates at the absolute difference between the tones (Oster, 1973; Moore, 2012). These beats are thought to originate subcortically in the medial nucleus of the superior olivary complex, the first nucleus in the auditory pathway to receive bilateral input (Wernick and Starr, 1968; Kuwada et al., 1979). This “illusory” third tone is lateralized between the left and right ear of the listener, making binaural beats useful for spatial sound research (Ross et al., 2014).

from the Wikipedia articles on Sound localization

The auditory system uses several cues for sound source localization, including time difference and level difference (or intensity difference) between the ears, and spectral information.

and Superior olivary complex

The superior olivary nucleus plays a number of roles in hearing. The medial superior olive (MSO) is a specialized nucleus that is believed to measure the time difference of arrival of sounds between the ears (the interaural time difference or ITD). The ITD is a major cue for determining the azimuth of sounds, i.e., localizing them on the azimuthal plane – their degree to the left or the right.

So there is sort of a clump of neurons in the brainstem that are specialized to integrate the timing information between the left and the right ear. I'm guessing those then start firing in the 40Hz frequency and that is interpreted as a third sound higher up.

I think this is ultra cool

You throw some energy at the brain in the right form (in this case some sound waves) and it reacts in some special way. If you put it like that, it is not surprising that some things eventually, end up doing something.

Now that the phenomenon has a name we can talk about it, similar to ASMR.

Thinking about how this was always part of how the brain works. Makes me wonder if some music composers and performers implicitly hit on this earlier, or not. I tried searching the web for

binaural beats in music

but I really get the same results mix as when I search for binaural beats.

Give me those binaural beats

I dint feel like opening a youtube video just to listen to some sounds. As an aspiring hacker and engineer, I just want to make my computer make some sounds that I want to listen to. So I decided to make a simple website that just makes binaural beats and nothing else.

The problem statement:

Make a sound on 1 ear (e.g. left headphone speaker)

Make a second sound on the other ear, with a +40Hz frequency.

scittle

I set up a beautiful hello world and connect with nrepl and get greeted with a friendly Isn't cool? this :).

It took me a moment to figure out I should open my index.html with a browser. Then do the cider-connect-cljs

1 ear, 1 sound

First I think let's start making a sound in 1 ear.

I am thinking a frequency that I can hear would be nice so I search the web for human auditory frequencies

1 kHz sounds like a good first thing to try. I am figuring something inside the speech area should sound natural (turned out that was a naive assumption).

The first playground

I have a file called playground.clj (Later I figured out I should rename to .cljs :) ).

Now getting heavily inspired by this code. Going 1 form by 1 like a usual lisp interaction dev experience, lulling it up:

(def ctx (js/window.AudioContext.))
(def ctx js/window.webkitAudioContext)

(def panner (ctx.createStereoPanner))
(set! (.. panner -pan -value) -1)
(.. panner (connect ctx.destination))

(def oscillators (atom []))
(let [o (ctx.createOscillator)
      _ (set! (.- o type) "sine")
      _ (set! (.. o -frequency -value) 1000)]
  (. o start)
  (. o (connect panner))
  (swap! oscillators conj o))

I connect my headphones via Bluetooth (a small feat on Linux, maybe another blog post), and lo and behold I hear a tone in my left ear. It is a really obnoxious sound, after a few seconds, it is so painful that I move the headphone to the side.

Playing Hz, first round

Here is a more endurable sound:

(set! (.. (@oscillators 0) -frequency -value) 200)

Playing around live at the REPL with sound is fun!

Now I am thinking, let's pick some music frequency, those are supposed to be crafted over hundreds of years to be beautiful frequencies, right?

Checking out a website. I pick D#3 / Eb3. Whatever that is.

(def a-note-freq 152.74)
(set! (.. (@oscillators 0) -frequency -value) a-note-freq)

binaural!

Let's do the thing where I add +40Hz and play that on the other ear. First I start generalizing how I make a panner:

(defn ->panner [ctx left?]
  (let
      [panner (. ctx createStereoPanner)
       _ (set! (.. panner -pan -value) (if left? -1 1))
       _ (.. panner (connect ctx.destination))]
    panner))

For some reason running this I get:

Failed to execute 'connect' on 'AudioNode': Overload resolution failed.

I went and asked in the nbb clojurians slack. I am sure one day we will figure out this error, in the meantime…

just put the second sound in the other ear, whatever

(ns beats)

(def a-note-freq 152.74)
(def binaural-beat-freq 40)
(def oscillators (atom []))
(defonce ctx (js/window.AudioContext.))

(def panners
  {:left
   (let [left? true]
     (let [panner (. ctx createStereoPanner)]
       (set! (.. panner -pan -value) (if left? -1 1))
       (.connect panner ctx.destination)
       panner))
   :right
   (let [left? nil]
     (let [panner (. ctx createStereoPanner)]
       (set! (.. panner -pan -value) (if left? -1 1))
       (.connect panner ctx.destination)
       panner))})

(defn oscillate [panner hz]
  (let [o (ctx.createOscillator)
        _ (set! (.- o type) "sine")
        _ (set! (.. o -frequency -value) hz)]
    (. o start)
    (. o (connect panner))
    (swap! oscillators conj o)))

(-> panners :right (oscillate (+ a-note-freq binaural-beat-freq)))
(-> panners :left (oscillate a-note-freq))

lol, it works.

Intermediate results

Artifacts

Every now and then there are artifacts in the sound with my headphones setup. Some are just rustling and crackling.

Others sound like some kind of metal being stretched far, far away, underwater, or in slow motion. It reminds me a bit of the Dungeon Keeper soundtrack. Dark and eerie, sort of slow mow.

Binaural beat?

Pretty sure I get the third-tone illusion. I would describe it as some kind of background or in between "airy" sound. It is supposed to seem to come from the middle of the brain. It is as if the fabric of the sound is richer. Like there is sound in more places.

Change the base frequency

It occurred to me, that if I get the 40Hz sound illusion, then I would predict that I can change the base frequency, keeping the 40Hz difference between left and right ear invariant. I should then constantly perceive a 3rd tone which is the binaural beat.

I really need a slider for frequencies in my life.

I did not do much web dev in my life and making a slider was a first.

I managed to put something together, drawing inspiration from w3 schools for the Html, scittle for how to export, and stackoverflow for how to add a function to "onchange".

nice dev experience, scittle really

  1. I did not need to restart my REPL at any point, adding those Html elements, then reloading my index.html - it handles that.
  2. I get auto completions with cider. E.g. js/.. does something.

    BTW here is a tip, for sci projects (babashka, nbb, scittle). Currently, you need to evaluate a namespace form first. Then you can get completions. At least with cider.

M8rSuZ3.png

Figure 2: A mouse with headphones typing on a laptop. Intelligent. Sparks of magic are flying around. There is lightning on the mouse and laptop. Computer programming source code. Colorful, playful.

The word scittle makes me think of a small mammal in the habitus of a mouse. I imagine it scurrying and curiously sniffing around.

code:

index.html

<!DOCTYPE html>
<html>
  <head>
    <script>var SCITTLE_NREPL_WEBSOCKET_PORT = 1340;</script>
    <script src="https://cdn.jsdelivr.net/npm/scittle@0.3.10/dist/scittle.js" type="application/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/scittle@0.3.10/dist/scittle.nrepl.js" type="application/javascript"></script>
    <script type="application/x-scittle" src="beats.cljs"></script>
  </head>
  <body>
    <div class="slidecontainer">
      <input type="range" min="120000" max="250000" value="15274"
      class="slider" id="frequencyRange" onchange="update_app(this.value)"> </div>
    <div id="frequencyDisplay"> </div>
  </body>
</html>

beats.cljs

(ns beats)

(def binaural-beat-freq 40)
(defonce ctx (js/window.AudioContext.))
(def slider (js/document.getElementById "frequencyRange"))
(def display (js/document.getElementById "frequencyDisplay"))

(def panners
  {:left
   (let [left? true]
     (let [panner (. ctx createStereoPanner)]
       (set! (.. panner -pan -value) (if left? -1 1))
       (.connect panner ctx.destination)
       panner))
   :right
   (let [left? nil]
     (let [panner (. ctx createStereoPanner)]
       (set! (.. panner -pan -value) (if left? -1 1))
       (.connect panner ctx.destination)
       panner))})

(defn update-display! [value]
  (set! (.-innerHTML display)
        (str "Base frequency: " value)))

(def get-oscillator
  (memoize
   (fn [panner]
     (let [o (ctx.createOscillator)]
       (set! (.- o type) "sine")
       (. o start)
       (. o (connect panner))
       o))))

(defn oscillate [panner hz]
  (let [o (get-oscillator panner)
        _ (set! (.. o -frequency -value) hz)])
  hz)

(defn update-app [frequency-value]
  (let [frequency-value (/ frequency-value 1000.0)]
    (-> panners :right (oscillate (+ frequency-value binaural-beat-freq)))
    (-> panners :left (oscillate frequency-value))
    (update-display! frequency-value)))

(set! (.-update_app js/window) update-app)

(update-app (* 1000 152.74))

The fact that I export my function by setting this global window object is quite wild. And then how I write a string of js code in the 'onchange' of the Html - damn. As I said, I am new to the web. Now I know one reason why it is called the wild west. But it let me "just do" what I wanted without being in my way so I appreciate that.

On the scittle side, I updated the code to only get 1 oscillator per slider. Seems like stuff is working. Switching up the frequencies via the slider is satisfying. For some reason, those artifacts are also gone. Maybe sliders just ensure balance and harmony in the world?

I am pretty sure I can tell there is 1 sound that seems to stay the same across frequencies.

I do the float * 1000 trick because I want to work with 2 digits for my frequencies.

Btw I also quickly tried what a real 40Hz tone sounds like. Ultra-low. I almost feel like I can make out the single waves, maybe that are the headphones cracking? Not surprising as the lower bound of human hearing is 20hz. The sound of the highest achievement of human thought. Genius insights, ideas that come to you like lightning. Globally integrating patterns of the brain and mind.

xijojsT.png

Figure 3: Harry potter wearing headphones having spiritual enlightenment about magic. Glowing magic sparks fly around. Colorful, cinematic, video game concept art.

Notes

Images

Made with stable-diffusion. The captions are the prompts I used.

Update 1

Currently does not work on mobile. Seems like I have the opportunity for another web dev feat, then. Seems like the issue is isolated on the sound appearing because the slider value works fine (meaning that the scittle code is running fine).

Update 2

Now it also works on mobile! Somebody helpfully pointed out on slack that on mac there was an error about audio refusing to play before the first touch event. I fixed that by adding a play button. But mobile still did not work, ok. After checking stackoverflow I still sort of have the idea that it has to do with user interaction events and this AudioContext. I first wanted to delay the whole audio setup until you click the button. But read that you should make a single AudioContext at the beginning.

So I try to delay the 1 other thing in the code:

(def panners
 (delay {:left (->panner true) :right (->panner false)}))
(-> @panners :right (oscillate (+ frequency-value binaural-beat-freq)))
(-> @panners :left (oscillate frequency-value))

It works, haha!

Date: 2022-09-19 Mon 11:04

]]>
https://faster-than-light-memes.xyz/jacking-nbb.html jacking-nbb 2022-10-14T23:59:59+00:00 jacking-nbb

:faster-than-light /memes

jacking-nbb

A cider jack in command for nbb.

Introduction

Nbb is a beatiful parasite. A node script that is an interpreter for Clojurescript. You get the upsides of Node.js + it is Clojure.

Want to try out some npm packages? Sure, just make a package.json, works instant, is part of the design principles of nbb.

You get Clojures immutable data structures, polymorphism constructs, a repl, etc.

On the tooling side, you might be surprised how far nbb gets with very little.

Scratching (in space) is very usefull and nbb is a natural fit for a quick cljs repl.

Update 2:

You can jack in Nbb nowadays with cider-jack-in-cljs then select nbb.

Alternatively, if you have cider d292d8d7eea5d3a12c138687133761a7256b1705

We can define jack in commands like so:

(defun cider-jack-in-nbb-2 ()
  "Start a Cider nREPL server with the `nbb nrepl-server` command."
  (interactive)
  (cider-jack-in '(:jack-in-cmd "nbb nrepl-server")))

You can also set a project jack-in-cmd in .dir-locals by setting cider-jack-in-cmd locally.

((nil
  (cider-jack-in-cmd . "nbb nrepl-server")))

This is especially useful, if you have an nbb project. You can now call cider-jack-in-clj without the need to select anything further anymore.

Note: Commands and elisp below are workarounds we do not need anymore because Cider supports Nbb much better since recently!

Update:

This is no longer needed with cider 1.6. There is a dedicated nbb cider build tool now. I still also think my proposal here would be useful.

;; the equivalent of the proposed change
(advice-add
 'cider--update-jack-in-cmd
 :before-until
 (defun cider-dont-update-jack-in-cmd-when-given (params)
   (when (plist-get params :jack-in-cmd) params)))

;; now a nbb jack in command becomes:
(cider-jack-in '(:jack-in-cmd "nbb nrepl-server"))

Jack-in command

cider repl workaround

We currently need this workaround for sci-based cljs repls for cider:

(cider-register-cljs-repl-type 'nbb-or-scittle-or-joyride "(+ 1 2 3)")

(defun mm/cider-connected-hook ()
  (when (eq 'nbb-or-scittle-or-joyride cider-cljs-repl-type)
    (setq-local cider-show-error-buffer nil)
    (cider-set-repl-type 'cljs)))

(add-hook 'cider-connected-hook #'mm/cider-connected-hook)

Note: You do not need this workaround in recent versions of cider anymore.

the command

Unfortunately, cider does not have the concept of a nbb repl currently. I think cider-jack-in-resolve-command and similar functions could be thrown out and replaced with data.

So I went 1 level deeper to bypass ciders auto-detection of project type (+ jack in cmd):

(defun mm/cider-jack-in-nbb ()
  "Start a nbb nrepl process and connect."
  (interactive)
  (let* ((cider-allow-jack-in-without-project t)
         (orig-buffer (current-buffer))
         (params '(:jack-in-cmd "nbb nrepl-server :port 0"
                   :cljs-repl-type nbb-or-scittle-or-joyride))
         (params (cider--update-project-dir
                  params)))
    (nrepl-start-server-process
     (plist-get params :project-dir)
     (plist-get params :jack-in-cmd)
     (lambda (server-buffer)
       (with-current-buffer
           orig-buffer
         (cider-connect-sibling-cljs
          params
          server-buffer))))))

This is what I wanted to say:

(defun mm/cider-jack-in-nbb ()
  "Start an nbb nrepl process and connect."
  (interactive)
  (cider-jack-in-cljs
   '(:jack-in-cmd "nbb nrepl-server :port 0"
     :cljs-repl-type nbb-or-scittle-or-joyride)))

hack for the remaining issue

With this, you should get an error "ClojureScript is not available…". Until there is a fix in cider, you can hack it by redefining cider-verify-clojurescript-is-present:

;;; FIXME: https://github.com/clojure-emacs/cider/issues/3255
(defun cider-verify-clojurescript-is-present ()
  "Check whether ClojureScript is present."
  (unless (nrepl-dict-get (cider-sync-tooling-eval "cljs.core/inc") "value")
    (user-error "ClojureScript is not available.  See https://docs.cider.mx/cider/basics/clojurescript for details")))

Results

Hehe, insta nbb repl. Works good.

Date: 2022-10-14 Fri 10:22

]]>
https://faster-than-light-memes.xyz/clojure-function-psychology-quiz.html Which Clojure Core Function are You? 2023-02-18T23:59:59+00:00 Which Clojure Core Function are You?

:faster-than-light /memes

Which Clojure Core Function are You?

Which Clojure Core Function are You?

Introduction

Since Chat-Gtp generating a certain kind of text is starting to be a solved problem.

I sort of liked what it had to say about what it means to be a fire bender in Avatar.

For some reason, I decided to make Clojure core function psychology puzzle, along the lines of What Clojure Function Are You? but also combine it with Avatar elements.

Avatar is cool

Is it just me or is there some Avatar The Last Airbender affinity in the Clojure ecosystem?

There is a database called Asami and Stuart Halloway is cosplaying as Aang somewhere.

Anyway, I dig Avatar myself, the whole world and the bending fit together very organically. Plus it sort of comes with thoughtfulness. And the bending, the choreography and the philosophy of it. Fire is anger but also life, it is about control. And with the highest control and focus you can lightning bend.

The bending choreography is amazing. Yes, I mean the choreography of the animated characters. It is inspired by Thai-Chi and other disciplines. Every element (fire, water, earth, air) has its style. It is one of the best magic systems I have encountered. The animations + sounds have a physical oomph. I almost feel like I know how it would be to bend. Water is fluent and dynamic, earth is solid and unmoving, fire is pure power, air is flying around and seeing the big picture.

Prompt engineering, Chat-Gpt makes a quiz

prompt 1

Help me make a quiz in the style of "Find out your harry potter house by answering those 6 psychological questions".

The quiz asks psychological questions that establish some personality traits of the user.

But we tell the user in the end which clojure.core function fits their personality.

Say one of those:

  • conj
  • reduce
  • juxt
  • swap!
  • into

We also map those with elements and bender power to Avatar the last airbender. For the bender psychological traits there are the elements and then there is another one for characters like Asami, who uses technology instead of bending.

Map the clojure core functions to avatar elements with a short description why.

prompt 1 end

I then kept asking a little, to get hiccup formatted data etc.

Result

I went and made a scittle re-frame app.

Code and link to the quiz!.

Date: 2023-02-18 Sat 16:55

]]>