screwlisp proposes a toast

Five notes about mooing in emacs with common lisp

| link | Last. (57 4 19 23 10 2026 4 T 8) |

This article on my personal kitten blog quilts together my recent writing aboud moo.el, LambdaMOO and slime common lisp in emacs, and is the canonical source for those bits of common lisp and emacs lisp code in codes for homing MOO demons in common lisp via moo.el’s emacs hook.

Rehoming myself firmly atop my kitten is obviously critically important ; kitten is better than ever with tls recently ceasing being conventionally involving “no-one-guessing-the-digit-4” and lots of people needing a powerful home to scurry to other than php.

As you could (but do not need to) check, this is heavily based on five orgfiles that I initially adapted to/presented on the malfunctioning shambolic birdsite-spinoff that is Medium. Thank-you to everyone pointing out to me that writing “for medium” was a no-go. The sloppagedon study that is Medium is still a target on my radar similar to how unionists say not to alienate employees of the worst companies, because they are some of the people who most know how important having a union would be. But I definitely need to syndicate to there from my own position of power. And kitten is soooo much better than whatever Medium’s web app is.

While my inchworming and somewhat fragmentary personal progress deserves this paragraph, I think it is fundamentally useful to see me gradually unpickling myself or conversely struggling to put on the sweater of MOO/lisp/demon programming however awkward and temporarily blindly. This vantage has the advantage that it I think lets you reasonably appraise that I am never slopping. There is a clear history of personal conundrums, ideas and stumbling progress. I try to densely pepper links I am thinking about or needed.

The cattle are lisping-MOO VR and lisp puppets

| link | Last. (1 6 19 23 10 2026 4 T 8) |

I realise that replacing lowing with lisping in Away in the Manger is going to annoy every conceivable demographic, not least the ones whom will say come on it is not even Halloween yet. But bovine boxcars will wait for no one. My gist here is that we are going to a. start a common lisp image, b. start a lambdacore classic MOO, and c. moo.el into it as usual. Then the contribution to the art will just be adding to an emacs moo-mode hook, a setter to a basic common lisp class.

Slime

I suppose you have a common lisp (say, ecl or sbcl or clisp or ccl or abcl or lispworks’ or allegrocl or clasp or 
) and the usual emacs mode slime, which you should reasonably clone from git and add-to-path in your ~/.emacs.d/init in the usual way. Note that sbcl could be said, “swashbuckle” as (1- (expt 2 36)) can and must be read “quicksilver” in base 36.

Slime: https://github.com/slime/slime

MOO

For our PersonalMOOs I think the right approach is to download lambdamoo latest and lambdacore latest from here: https://lambda.moo.mud.org/pub/MOO/

(i.e. the moo program to build as it was at 15 years of age in 2005, and the corresponding moo database). You must build it. I suppose you might need yacc and unicode and regex ; it does not seem like it will be a problem but also see commentary still in wrog’s 1.9 new version: https://github.com/wrog/lambdamoo .

The shell invocation to start it is then

moo last.db next.db

and to use moo.el you:

M-x mud-add-world <enter> PersonalMOO <enter> <enter> <enter> localhost <enter> 7777 <enter>

followed by:

M-x mud <enter> Pers <tab> <enter>

.

Moo.el

yduJ has upped moo.el. https://codeberg.org/nosrednayduj/moo-el

http://www-cs-students.stanford.edu/~yduj/

Common lisp side

Over in slime I

(uiop:define-package :moo
  (:export #:output-line #:*player* #:player))
(defclass moo:player () ((moo:output-line
			  :type 'string
			  :writer moo:output-line)))

(defvar moo:*player* (make-instance 'moo:player))

(defmethod
    (setf moo:output-line)
    (moo::line (obj moo:player))
  (setf (slot-value obj 'moo:output-line)
	moo::line))

I suppose the redefinition of the setter is somewhere between redundant and actively bad, but it seems mnemonic to remind a maybe fledgling lisper what a writer is.

MOO.el fixen

I found, in my PersonalMOO buffer I had to manually start the history and set the prompt character and hook:

(mud-initialize-input-history)
(put 'MOO 'prompt ?>)
(put 'MOO 'filters 'moo-filter-hook)

as a stop measure, future patches notwithstanding.

emacs lisp

Now in emacs:

(defun mud2slime-line-at-point (&rest rest)
  (goto-char (point-min))
  (let ((l (thing-at-point 'line t)))
    (slime-eval-save
     (format
      "(setf (moo:output-line moo:*player*) %S)"
      l))))
(add-hook
 'moo-filter-hook
 #'mud2slime-line-at-point
 '90)

MOO output lines now SETF (moo:output-line moo:player)

I initialized it to “foo” manually.

MOO> (setf (output-line *player*) "foo")
"foo"
MOO> (slot-value *player* 'output-line)
"foo"

in the moo:

:dances
Wizard dances

and so:

MOO> (slot-value *player* 'output-line)
"Wizard dances
"

and I am wrapping it up here because this is a glorious and complete victory. It strikes me though that you might not have read Tietelman on advice in the 1960s.

The reason I gave that slot a writer and not an accessor is that I do not intend the slot to be used in general: However, we can of course add :after method [mixins
] to moo:player’s class:

MOO> (defvar *count* 0)
*COUNT*
MOO> (defmethod (setf output-line) :after (line (obj player))
       (incf *count*))
WARNING:
   redefining (SETF OUTPUT-LINE) :AFTER (#<SB-PCL:SYSTEM-CLASS COMMON-LISP:T>
                                        #<STANDARD-CLASS MOO:PLAYER>) in DEFMETHOD
#<STANDARD-METHOD (COMMON-LISP:SETF MOO:OUTPUT-LINE) :AFTER (T PLAYER) {1205962C43}>

Here, I whistled three times in the MOO:

>:whistles
Wizard whistles
>:whistles
Wizard whistles
>:whistles
Wizard whistles
>

and check the count in lisp:

MOO> *count*
3
MOO> 

And so we see we can lisp anything that we want with secret alien technology.

The real encores, but I am too exhausted right now, is to derive a new class for moo:player which is a child of clim-lisp:standard-application-frame at which point things get crazy for real. Or have common lisp puppet the MOO player connection in response to a stray output. Maybe that would be a good moonivresary activity. (As you recall, LambdaMOO virtual reality started up on Oct 30 1990).

Thank-you everyone for appreciating that picture of a lake I met up with a cousin at in northern Ontario while I am away! My most popular Mastodon toot ever[1], except for the time that parrot tricked me out of an ANZAC biscuit[2].

https://gamerplus.org/@screwlisp/117411883143477687 https://mastodon.sdf.org/@screwtape/109548530399562160

VR and UI with local LambdaMOO and LISP CLIM

| link | Last. (40 24 21 23 10 2026 4 T 8) |

A corecursive UX in practice

Speech and actions in VR dynamically evolving an external GUI frame (window) and the external GUI frame performing speech and actions in the VR and these recursively driving each other seems like the bleeding edge of not just user experience but human computer interfacing at all to me now. At this point basically everyone codes and the interesting resolution - and a resolution we do not have - is between codes that are gud and codes from those that still need to git gud. By way of explanation the problem is not that the coder does not yet know how to code, it is that the utility of the computing experience they are producing for themselves and others is bad. I am always keenly aware of a computer person telling me proudly about themselves that

If you tell me exactly what to do I can program anything!

Imagine another author telling you that if you tell them exactly what to write (and pay them for it) they can author anything. This is a fundamental misapprehension of skill at computer touching. It should be possible to give advice to improve one’s and one’s production of others’ computer user experiences and my labor and exploration here is to replace an aspiration of coding good with a different and improved practice.

About mooing

Mooing is a text based virtual reality? Sounds like a lot of typing. Let me back you up here a bit. Relative to the internet at large, a huge proportion of MOO test-based VR users use text2speech to read and speech2text to write. For this reason by dint blind users are especially well-represented around it. Looping conventional speech and listening (as appropriate) into practical computer useage puts us miles ahead of the computing curve. However this is not my focus in this article. It is just that MOOing, like my writing here, has a basic textual view of information: when in fact in-MOO, real speech and listening are much more prevalent in MOO VR interfaces than in url-typing web users.

About the common lisp interface manager spec

First, I am not showing you a stupid pre-fab app made-by-somebody-else that you can buy. Hence there is some coding. In fact wedding a careful choice of codes to a rich virtual reality experience is what we are doing here. Not everyone who wants you to pay into their product by becoming their user and unpaid promoter is actually trying to give you genuinely constructive commentary. The 24th-most-popular-in-2025 programming language was a 33 year old ANSI standard, ANSI common lisp. LISP was the AI language created by John McCarthy in 1956.

Anyway, that is the one we are using. Interestingly, and about one year older than ANSI common lisp is a specification of rich multimedia interfaces and gesture-command presentations created by a collaboration of independent industry forces for ANSI common lisp. I dunno, read the wikipedia page if you are interested. The gist is that we are going to be attaching the textual view of real-time virtual reality dynamically (in the technical sense) to dynamic (in the technical sense) commands arriving at our CLIM application-frame (GUI window). Technically, this inhabits a lisp image cohabitating with our MOO client in an emacs, but that is somewhat incidental useage.

A simple graph gui (just the clim spec)

I am not here trying to teach you to lisp, but it is better for you to literally know what the gui-frame/command/generic-multiple-dispatch portion literally is. The tiny amount of emacs-based prior setup, which this article is not about, is from this article over here []. By all means ask me for another article about the lisp.

  (require "mcclim")
  (in-package :clim-user)
  (defclass gui-graph
      (moo:player)
    ((graph-root
      :initarg :graph-root
      :accessor graph-root))
    (:default-initargs :graph-root '(start)))

  (defmethod add-node
      (new-node (obj gui-graph))
    (with-slots
	  (graph-root output-line)
	obj
      (do*
       ((last-tail nil tail)
	(tail graph-root (cadr tail)))
       ((null tail)
	(nconc last-tail `((,new-node)))))))

  (define-application-frame graph-frame
      (gui-graph standard-application-frame)
    ()
    (:pane :application
     :display-function 'display-graph))

  (defmethod display-graph
      ((f graph-frame) (p stream))
    (format-graph-from-root
       (graph-root f)
       #'(lambda (x s) (present* (car x) s))
       #'cdr
       :orientation :horizontal))

  (define-graph-frame-command
      (com-maybe-add-node :menu t)
      ((new-line 'string))
      (let* ((trigger "add-node ")
	     (len (length trigger))
	     (idx (search trigger new-line)))
	(when
	    idx
	  (add-node
	   (subseq
	    new-line
	    (+ idx len)
	    (1- (length new-line)))
	   *application-frame*))))

  (defmethod
      (setf moo:output-line)
      :after
      (line (obj graph-frame))
    (execute-frame-command
     obj
     `(com-maybe-add-node ,line)))

Victory

As you can see, on every line of output from the moo, moo:*player*'s slot moo:output-line is set to that line.

Then :after any time moo:output-line is set, com-maybe-add-node is run. If it found "add-node " it adds the rest of the line to the linear graph.

I am not fancying it up here at all: our purpose was simple to show that e.g. a string appearing in a line of MOO output dynamically updates our application-frame (graphical display), hooked via emacs moo.el and slime (ANSI common lisp).

Conclusions and further work

This extends the user experiential gesture concept to

any identifier dynamically appears in the textual view of Virtual Reality cues any command on your gui frame

I understand that many later groups copied lisp-style “command translator” gestures, often without the participants knowing the lisp their art came from.

While my example is suitably complete and minimal (allowing, my set-up post I linked which is also minimal) there are several extremely high-value proximal moves:

I would deeply appreciate comments as to what you found interesting and would like me to prioritize writing about: if you contribute and share yourself that would be wonderful.

Adding more specific moo (mud) VR emacs slime common lisp hooks

| link | Last. (20 51 19 23 10 2026 4 T 8) |

Allowing our private virtual reality to drive lisp and lisp to drive our private virtuality

We are in emacs, MOOing via yduJ’s upping of her moo.el including KMP’s recent patch, and lisping with slime. Then in lisp, (moo:moo “emote somethings”) for example would for example cause wizard somethings in the moo. On the moo side , <lisp> <nonsemantic identifier characters> (setq foo 'bar) reads and then evals the rest-of-the-line form. This is intended for our personal pocket dimension VR or otherwise something of ours without read permissions in a community moo. The lisp side invoked from the MUD can of course moo:moo itself. moo:moo requires (setq slime-enable-evaluate-in-emacs t) in emacs. Combined with lisp’s sharpsign dot, lisp variables can be sent into the MOO.

Setup

I made a git scroo. You should clone it into ~/.emacs.d/ and then put (load "~/.emacs.d/scroo/scroo.init.el") into your emacs init, and (load "~/.emacs.d/scroo/scroo.lisp") in your slime repl. You need to do two things explicitly:

  1. M-: (setq slime-enable-evaluate-in-emacs t) <ret> in emacs
  2. (change-class moo:*player* 'moo:lisps) in slime

Where the latter allows the <lisp> lines from the moo and the former makes moo:moo work.

Why the funny characters?

The security here is not broad spectrum cybersecurity but is that I did not want any semantic typing of characters to be being used to choose where to direct the line-to-be-processed to. So the way I did it is that I generated ten random characters:

A common lisp loop outputting ten random characters

  (loop
    initially (setq *random-state* (make-random-state t))
    with characters-sought
      = 10
    for r = (random (ash 1 7))
    for ch = (code-char r)
    when (and (not (char= ch #space))
	      (graphic-char-p ch))
      collect ch
	into chs
    when (> (length chs) characters-sought)
      return (mapc 'princ chs))

=>

: gQ@.40vHD8M

Different fixed string identifiers

Then I combined this string with “AA” salt in the MOO:

	  ;crypt("gQ@.40vHD8M sbcl", "AA");

to get

  (lispalist '(("mIFIHwnD8jU"
		. "*slime-repl sbcl*")))

and this and that cond could be customized to send more things more places.

I chose this nonsemantic identifier scheme for “eval in sbcl” (or add an sbcl<2>, ecl, 
 
). Since the characters are gibberish, you should never want to look at them; they should just live inside a nonreadable property in the MOO. Technically the right thing to do is to just store “gQ@.40vHD8M” in the MOO property and using crypt properly with random salt, but in a private context this seemed sufficient and more obvious / expedient.

I am not considering this seaworthy outside of our PersonalMOO right now. The mnemonic is that inside your personal dimension you are arbitrarily powerful; in a communal MOO you are just another player.

Output example

>;me:tell("<lisp> "AAmIFIHwnD8jU" (moo::moo ":dances")");
<lisp> "AAmIFIHwnD8jU" (moo::moo ":dances")
=> 0
Wizard dances
>;me:tell("<lisp> "AAmIFIHwnD8jU" (moo::moo #.(format nil ":~a" *bar*))");
<lisp> "AAmIFIHwnD8jU" (moo::moo #.(format nil ":~a" *bar*))
=> 0
Wizard BAZ
Wizard juggles
>

As we can see

  1. I send myself the magic <lisp> line in-MOO 
 telling lisp to tell the moo (here) to :dances i.e. emote dancing and we can see Wizard dances.
  2. Sending similarly, but the line to read uses lisp’s sharpsign-dot i.e. read-eval feature to build a MOO emote from a lisp variable, uninterestingly Wizard BAZ - where BAZ was the value of the named variable bar in lisp.
  3. Wizard juggles without apparent input - it came from (moo:moo “:juggles”) happening in lisp.

Conclusions

I think this hit everything, though two changes I will need to make will be to make the identifier gibberish dotted to emacs buffer names an emacs customize-variable alist and possibly use MOO’s crypt more properly dynamically; and I should try and figure out some suitable-for-multiplayer-moo sensibilities, considering that MOOing happens over telnet.

Reconnecting with my McCLIM useage before to have MOO objects maintaining an external GUI and otherwise rich media output semantically matching MOO objects and their properties (taking a Ted Nelson’s interactable icon view of MOO objects) probably focused around/about the player me having lisp’s gesture-based inputs - I am thinking touchscreen based moreso than mouseful. Some kind of MOO virtuality GUI CLIM lisp machine front-end.

Once all of this is working (and it is already working), I think I might go through Winograd’s Understanding Natural Language 1972 and other books, in light of Adler and Winograd’s Usability books/essays. I think this will be reasonable to get into some popular Medium publications as well.

Anyone reading this spun up their personalMOO using this or at all? Would love to hear from you here and or on the Mastodon.

Fancy lisp interaction in MOO VR

| link | Last. (24 26 20 23 10 2026 4 T 8) |

moo object with a verb that calls lisp that calls into the MOO

In this case, I show my PersonalMOO setup with making a scrambler object in-VR and programming it to ask lisp to call a lisp function, scramble which sets a property on the scrambler MOO-side. I named the scrambler “word (un)scrambler” but I ended up only implementing the scrambling half. This was the first time I tried using my own MOO setup, whatever the deeper meaning of that is!

Since it obviously works, the sort-of behind-the-scenes outputs are not really a problem, since they will ultimately be performed by an unattended demon-player (screwbot here, though I just used screwlisp in this first example), like Emily in jlamothe’s Sushi Paradise in lambda.moo.mud.org.

Since I am basically skipping a conclusion here, here are some links I used:

I have put the action and the setup in reverse order to in media res.

Logging in

>Welcome to the LambdaCore database.

Type 'connect wizard' to log in.

You will probably want to change this text and the output of the `help'
command, which are stored in $login.welcome_message and $login.help_message,
respectively.
Usage:  connect <existing-player-name> <password>
>co screwlisp sybAj
,*** Connected ***
The First Room
This is all there is right now.
You have new mail (1 message).  Type 'help mail' for info on reading it.
This is your first time connected.
There is new news.  Type `news' to read all news or `news new' to read just new
news.
>@nn
Message 1 on screwlisp (#98):
Date:     Sat Oct 17 12:32:10 2026 PDT
From:     Wizard (#2)
To:       *New-Prog-Log (#29) and screwlisp (#98)
Subject:  @programmer screwlisp (#98)

I just gave screwlisp a programmer bit.
--------------------------
>

We can see the wizard @programmered me.

Creating a MOO $thing object at all

I guess a $thing is something which can be picked up, put down, thrown
 My idea is a word unscrambling game. So when you enter a word in the device in the MOO, its letters get knuth-shuffled in lisp. Then the game is to swap letters until it is back to the original.

>@create $thing named word (un)scrambler
You now have word (un)scrambler with object number #99 and parent generic thing
(#5).
>drop #99
You drop word (un)scrambler.
>look here
The First Room
This is all there is right now.
You see word (un)scrambler here.
>

Add goal and current properties

Using the object number since I think the space in its name makes the @property call ambiguous.

>@property #99.answers {}
Property added with value {}.
>@property #99.scramble ""
Property added with value "".
>@prop #99.lisp_prefix "<lisp> "AAmIFIHwnD8jU" "
Property added with value "<lisp> "AAmIFIHwnD8jU" ".
>

When goal is set, lisp sets current to the goal shuffled

Lisp side:

  (defun scramble (word) 
    (moo:moo (format nil ";#99.scramble = ~s;"
		     (alexandria:shuffle word)))
    (moo:moo (format 
	      nil 
	      ";me:tell("You see the letters scramble.");")))

Moo side:

>@verb "word (un)scrambler":type any in this
Verb added (1).
>@program #99:type
Now programming word (un)scrambler:type(1).
[Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
>player:tell(this.lisp_prefix + "(scramble "" + dobjstr + "")");
>this.answers = $list_utils:remove_duplicates(listappend(this.answers, dobjstr));
>player:tell("You type up '" + dobjstr + "' on the word (un)scrambler and hit return.");
>.
0 errors.
Verb programmed.

Scrambling:

>type foobar in word (un)scrambler
<lisp> "AAmIFIHwnD8jU" (scramble "foobar")
You type up 'foobar' on the word (un)scrambler and hit return.
=> "oorbfa"
You see the letters scramble.
=> 0
>

Well, it works as I said.

Setup again

You have to set something up! I ran the elisp in emacs and lisp in slime from over before[]. I started mooing in a shell with lambdacore as detailed before (don’t forget (setq slime-enable-evaluate-in-emacs t)). And manually added a MOO target to connect to as per usual:

M-x mud-add-world
PersonalMOO


localhost
7777
MOO

and started mooing with M-x mud Pers<ret>! Once in I issued my M-x start-mooing setup helper. Here I suppose is the particular setup I did in-moo, provided as a transcript. I suppose you can figure it out or ask:

>co wizard
,*** Connected ***
The First Room
This is all there is right now.
Last connected Fri Oct 16 11:08:57 2026 PDT from localhost
>@make-player screwbot nil
'nil' doesn't look like a valid internet email address.
Create character anyway?  [Enter `yes' or `no']
>yes
screwbot (#97) created with password `QaVEX' for nil
Sorry, the network isn't active.
*Player-Creation-Log (#17) has just been sent new mail by Wizard (#2).
>@programmer screwbot
@Programmer screwbot despite its lack of description? [Enter `yes' or `no']
>yes
screwbot (#97) is now a programmer.  Its quota is currently 20000.
screwbot and the other wizards have been notified.
*New-Prog-Log (#29) has just been sent new mail by Wizard (#2).
>@make-player screwlisp nil
nil is the registered address of the following characters:
screwbot (#97) []
-There has already been a character with that or a similar email address.
Create character anyway?  [Enter `yes' or `no']
>yes
screwlisp (#98) created with password `sybAj' for nil
Sorry, the network isn't active.
*Player-Creation-Log (#17) has just been sent new mail by Wizard (#2).
>@programmer screwlisp
@Programmer screwlisp despite its lack of description? [Enter `yes' or `no']
>yes
screwlisp (#98) is now a programmer.  Its quota is currently 20000.
screwlisp and the other wizards have been notified.
*New-Prog-Log (#29) has just been sent new mail by Wizard (#2).
>@quota screwbot is +100000
screwbot's quota set to 120000.
*Quota-Log (#34) has just been sent new mail by Wizard (#2).
>@quota screwlisp is +100000
screwlisp's quota set to 120000.
*Quota-Log (#34) has just been sent new mail by Wizard (#2).
>@measure new screwbot
Measuring the sizes of screwbot's recently created objects...
Measured screwbot (#97):  2834 bytes.
Total bytes used in new creations: 2834.
>@measure new screwlisp
Measuring the sizes of screwlisp's recently created objects...
Measured screwlisp (#98):  2839 bytes.
Total bytes used in new creations: 2839.
>@quit
,*** Disconnected ***

I thought that I might want a me-player and an automated-service-player, but in this case I think I just barely want to see my moo <-> moo.el <-> slime <-> clim application-frame emacs flow flowing.

MOO VR LISPM

| link | Last. ____ |

I had to delete ‘fantasy’ from the title because it is real

This is the natural and key progression of my recent toots showing MOO VR driving LISP driving MOO VR. Instead of having esoteric tags visible to the user, there is just a lispm object. A player sends strings to the lispm object. A demon - screwbot - is notified to send the string to the external lisp implementation. The returns and any output are set on the lispm properties.

Screwbot’s moo.el connection

M-x mud-add-world
PersonalMOO-bot
<ret>
<ret>
localhost
7777
MOO

Start local lambdaMOO

$ ./MOO-1.8.1/moo db/003.db db/004.db

003 is my Personal LambdaCore db evolution with screwlisp and screwbot programmer users from the last article.

Starting slime lisp

Emacs lisp:

  (slime)
  (setq slime-enable-evaluate-in-emacs t)
  (load "~/gits/scroo/scroo.init.el")

In there:

  (load "~/gits/scroo/scroo.lisp")
  (change-class moo:*player* 'moo:lisps)

Enabling lisp to drive emacs and changing moo:*player* to be a moo:lisps is how I am having the lisp image be able to autonomously drive the moo.

Log screwbot in

M-x mud
PersonalMOO-bot

and use my start-mooing:

M-x start-mooing

and connect:

co screwbot QaVEX
*** Connected ***
The First Room
This is all there is right now.
You see word (un)scrambler here.
You have new mail (1 message).  Type 'help mail' for info on reading it.
This is your first time connected.
There is new news.  Type `news' to read all news or `news new' to read just new
news.
>@nn
Message 1 on screwbot (#97):
Date:     Sat Oct 17 12:29:29 2026 PDT
From:     Wizard (#2)
To:       *New-Prog-Log (#29) and screwbot (#97)
Subject:  @programmer screwbot (#97)

I just gave screwbot a programmer bit.
--------------------------
>

investigate screwbot:

>@show me
Object ID:  #97
Name:       screwbot
Parent:     generic programmer (#58)
Location:   The First Room (#62)
Owner:      screwbot (#97)
Flags:      player programmer
<truncated>

Since I am just going to hardcode screwbot’s object ID in the lispm.

Log screwlisp in

M-x mud-add-world
PersonalMOO
<ret>
<ret>
localhost
7777
MOO

and M-x mud PersonalMOO and connect (oops I got ahead of myself and made/moo-programmed the lispm here too):

  >co screwlisp sybAj
  *** Connected ***
  The First Room
  This is all there is right now.
  You see word (un)scrambler here.
  screwbot is here.
  Last connected Sat Oct 17 15:55:47 2026 PDT from localhost
  There is new news.  Type `news' to read all news or `news new' to read just new
  news.
  >@create $thing named lispm
  You now have lispm with object number #100 and parent generic thing (#5).
  >drop lispm
  You drop lispm.
  >@property lispm.standard_output ""
  Property added with value "".
  >@property lispm.last_input ""
  Property added with value "".
  >@property lispm.last_error ""
  Property added with value "".
  >@property lispm.returns {}
  Property added with value {}.
  >@verb lispm:run this on any
  Verb added (1).
  >@program lispm:run
  Now programming lispm:run(1).
  [Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
  >#97:tell(this.lisp_prefix + "(moo-run "" + iobjstr + "")");
  >player:tell("The secret alien technology takes your input.");
  >.
  0 errors.
  Verb programmed.
  >@prop lispm.lisp_prefix "<lisp> "AAmIFIHwnD8jU" "
  Property added with value "<lisp> "AAmIFIHwnD8jU" ".
  >@program lispm:run
  Now programming lispm:run(1).
  [Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
  >#97:tell(this.lisp_prefix + "(moo:moo-run "" + iobjstr + "")");
  >player:tell("The secret alien technology takes your input.");
  >.
  0 errors.
  Verb programmed.
  >

better add moo:moo-run on the lisp-side

  (defmacro moo:moo-run
      (iobjstr)
    `(let*
	 ((proc "*PersonalMOO-bot*")
	  (rets)
	  (out)
	  (err
	    (ignore-errors
	     (setq
	      out
	      (with-output-to-string
		  (*standard-output*)
		(setq
		 rets
		 (multiple-value-list
		  (with-input-from-string
		      (*standard-input* ,iobjstr)
		    (eval (read))))))))))
       (moo:moo
	(format
	 nil
	 ";#100.returns = {~{"~s"~^, ~}};"
	 rets)
	proc)
       (moo:moo
	(format
	 nil
	 ";#100.standard_output = ~s;"
	 out)
	proc)
       (moo:moo
	(format
	 nil
	 ";#100.last_error = ~s;"
	 err)
	proc)
       (moo:moo
	(format
	 nil
	 ";#100.location:announce(~s);"
	 "The lispm's display lights up enticingly.")
	proc)))

Make a lispm

Okay, that was a slightly educational experience. It turns out that screwbot needs to own the object to interact with its properties, which are not public so I had the wizard transfer ownership with $wiz_utils:set_owner. But then my player, screwlisp could not simply look at the properties any more so screwbot added the verb lispm:check and lispm:get_returns().

  >run lispm on (values 1 "2")
  The secret alien technology takes your
  input.
  The lispm's display lights up
  enticingly.
  >check lispm
  The returns panel includes:
  1
  2
  >
  >;#100:get_returns();
  => {1, 2}
  >

Code for check:

>@verb lispm:check this none none
Verb added (2).
>@program lispm:check
Now programming lispm:check(2).
[Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
>player:tell("The returns panel includes:");
>for s in (this.returns)
>  player:tell(s);
>endfor
>.
0 errors.
Verb programmed.
>

Code for get_returns():

>@verb lispm:get_returns tnt
Verb added (3).
>@program lispm:get_returns
Now programming lispm:get_returns(3).
[Type lines of input; `?' for help; end with `.', `@abort' or `@edit'.]
>return this.returns;
>.

Yet more Conclusions

While there is certainly lots to do and later redo (something is going wrong with sending strings actually), basically I have a lisp machine in my lambdacore PersonalMOO VR at this point and hence can just write and use pretty arbitrary lisp programs in-MOO.

In particular, I am looking forward to having “Ted Nelson Icon” MOO object classes, i.e. pictures whose pieces are manipulated by gestures, and have these syncronise with the MOO virtuality in both directions.

Talk on the Mastodon thread link I will put in the comments please. Oh, if you are reading this - having Jeff Shrager with Kent on the show on Tuesday-night-in-the-Americas (0UTC “Wednesday”). Please tell as many people as you can, since so far I have missed episodes while overseas.

codes

| link | Last. (48 14 22 23 10 2026 4 T 8) |

Useage

These two files grew organically for me to use moo.el ( https://codeberg.org/nosrednayduj/moo-el ). I suppose scroo.init.el should be loaded in your emacs init file, and scroo.lisp can be loaded (load not asdf:load-systemed) in whatever common lisp slime repl you have open. slime is the superior lisp interaction mode for emacs ( https://en.wikipedia.org/wiki/SLIME ).

Then, I guess you M-x mud-add-world and specify the MOO you are connecting to, the M-x mud MyNamedMOO from that mud-add-world, then you M-x start-mooing which configures some MOO properties and adds a hook relating to slime.

Now for the ‘danger mode’ of letting MOO output drive lisp via emacs, you need to M-: (setq slime-enable-evaluate-in-emacs t) and in slime, (change-class moo:*player* 'moo:lisps). This is basically and specifically to have MOO demons whose brains and abilities basally live in a common lisp slime repl rather than just in the MOO. It is not intended to widely drive emacs, though it needs to communicate back to the MOO via emacs.

emacs lisp Elisp side

;;;; scroo.init.el
(defun mud2slime-line-at-point (&rest rest)
 (goto-char (point-min))
 (let*
     ((li (thing-at-point 'line t))
      (l (cl-subseq li 0 (1- (length li))))
      (lispline "<lisp> "AA")
      (lisplinelen (length lispline))
      (sortasecret "gQ@.40vHD8M")
      (lispalist '(("mIFIHwnD8jU"
		    . "*slime-repl sbcl*"))))
   (cond
    ((and
      (> (length l) lisplinelen)
      (cl-search """ (cl-subseq
		       l 0 lisplinelen))
      (equal lispline
	     (cl-subseq l 0 lisplinelen)))
     (let
	 ((keystring
	   (cl-subseq
	    l lisplinelen
	    (+ lisplinelen
	       (cl-search
		"""
		(cl-subseq
		 l lisplinelen))))))
       (with-current-buffer
	   (cdr (cl-assoc keystring
			  lispalist
			  :test 'equal))
	 (slime-eval-save
	  (format
	   "(moo:do-lisp moo:*player* %S)"
	   (cl-subseq l
		   (+
		    (cl-search keystring l)
		    (length keystring)
		    2)))))))
    (t
     (slime-eval-save
      (format
       "(setf (moo:output-line moo:*player*) %S)"
       l))))))

(defun start-mooing ()
  "Once in *PersonalMOO* M-x start-mooing <ret>
Not idempotent."
  (interactive)
  (mud-initialize-input-history)
  (put 'MOO 'prompt ?>)
  (put 'MOO 'filters 'moo-filter-hook)
  
  (add-hook 'moo-filter-hook
	    #'mud2slime-line-at-point
	    '90))

and common lisp side

;;;; scroo.lisp
(uiop:define-package :moo
    (:export #:output-line #:*player* #:player
	     #:lisps #:do-lisp #:moo #:moo-run))
(defclass moo:player () ((moo:output-line
 :type 'string
 :writer moo:output-line)))
(defvar moo:*player* (make-instance 'moo:player))
(defmethod
 (setf moo:output-line)
 (moo::line (obj moo:player))
 (setf (slot-value obj 'moo:output-line)
       moo::line))

(defclass moo:lisps
    (moo:player)
  ()
  (:documentation
   "moo:lisps is dangerous moo:player - for your PersonalMOO only.
Execute the rest of the line as arbitrary lisp as a side effect.

The association with a MOO crypt() string - alist
is just to prevent accidental calls in this case."))

(defmethod
    moo:do-lisp
    ((obj moo:lisps)
     str)
  (with-input-from-string
      (*standard-input* str)
    (eval (read))))

(defun moo:moo
    (str &optional (proc "*PersonalMOO*"))
  "str is like moo.el mud-send-string to proc"
  (swank:eval-in-emacs
   `(mud-send-string
     ,str
     ,proc)
   t))

(defmacro moo:moo-run
    (iobjstr)
  `(let*
       ((proc "*PersonalMOO-bot*")
	(rets)
	(out)
	(err
	  (ignore-errors
	   (setq
	    out
	    (with-output-to-string
		(*standard-output*)
	      (setq
	       rets
	       (multiple-value-list
		(with-input-from-string
		    (*standard-input* ,iobjstr)
		  (eval (read))))))))))
     (moo:moo
      (format
       nil
       ";#100.returns = {~{~s~^, ~}};"
       rets)
      proc)
     (moo:moo
      (format
       nil
       ";#100.standard_output = ~s;"
       out)
      proc)
     (moo:moo
      (format
       nil
       ";#100.last_error = ~s;"
       err)
      proc)
     (moo:moo
      (format
       nil
       ";#100.location:announce(~s);"
       "The lispm's display lights up enticingly.")
      proc)))
screwlisp proposes a toast