Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

2026 Participants: Martin Bartelmus * David M. Berry * Alan Blackwell * Gregory Bringman * David Cao * Claire Carroll * Sean Cho Ayres * Hunmin Choi * Jongchan Choi * Lyr Colin * Dan Cox * Christina Cuneo * Orla Delaney * Adrian Demleitner * Pierre Depaz * Mehulkumar Desai * Ranjodh Singh Dhaliwal * Koundinya Dhulipalla * Kevin Driscoll * Iain Emsley * Michael Falk * Leonardo Flores * Jordan Freitas * Aide Violeta Fuentes Barron * Erika Fülöp * Tiffany Fung * Sarah Groff Hennigh-Palermo * Gregor Große-Bölting * Zachary Horton * Dennis Jerz * Joey Jones * Titaÿna Kauffmann * Haley Kinsler * Todd Millstein * Charu Maithani * Judy Malloy * Eon Meridian * Luis Navarro * Collier Nogues * Stefano Penge * Marta Perez-Campos * Arpita Rathod * Abby Rinaldi * Ari Schlesinger * Carly Schnitzler * Arthur Schwarz * Haerin Shin * Jongbeen Song * Harlin/Hayley Steele * Daniel Temkin * Zach Whalen * Zijian Xia * Waliya Yohanna * Zachary Mann
CCSWG 2026 is coordinated by Lyr Colin-Pacheco (USC), Jeremy Douglass (UCSB), and Mark C. Marino (USC). Sponsored by the Humanities and Critical Code Studies Lab (USC), the Transcriptions Lab (UCSB), and the Digital Arts and Humanities Commons (UCSB).

Palimpsest vs Sketchpad

Mark and co. have asked me to suggest a code critique linked to my book Moral Codes.

First is an excerpt from the utility file “BoundingBox.java", which is part of ~60k lines of Java source code that implemented my Palimpsest language and environment (discussed in Moral Codes chapter 8, pp 112-113).

   public void alignBottomLeft(BoundingBox destination) {
       x = destination.x;
       y = destination.getBottom() - height;
   }

   public void alignLeftCentre(BoundingBox destination) {
       int newX = destination.x;
       alignCentreTo(destination);
       x = newX;
   }

   public void alignTopRight(BoundingBox destination) {
       x = destination.getRight() - width;
       y = destination.y;
   }

   public void alignCentreTo(BoundingBox destination) {
       Location newCentre = destination.getCentre();
       setLocation(newCentre.withoutOffset(new Offset(getSize().scaledBy(0.5))));
   }

I’d like to contrast that snippet with two short passages from Ivan Sutherland’s Sketchpad thesis (discussed in Moral Codes chapter 6, pp 80-83):

From Chapter III, p. 42:

HUMAN REPRESENTATION OF RING STRUCTURE
In representing ring structures the chickens should be thought of as beside the hens, and perhaps slightly below them, but not directly below them. The reason for this is that in the ring registers, regardless of whether in a hen or a chicken, the left half of one register points to another register whose right half always points back. By placing all such registers in a row, this feature is clearly displayed. Moreover, the meaning of placing a new chicken “to the left of” an existing chicken or the hen is absolutely clear. The convention of going “forward” around a ring by progressing to the right in such a representation is clear, as is the fact that putting in new chickens to the left of the hen puts them “last,” as shown in Figure 3.2. Until this representation was settled on, no end of confusion prevailed because there was no adequate understanding of “first,” “last,” “forward,” “left of,” or “before.”

And from Appendix D, p. 127

The macro instructions listed in this appendix are used to implement the basic ring operations listed in Chapter III. Only the format is given here since to list the machine instructions generated would be of value only to persons familiar with the TX-2 instruction code.

[…]

Take N of XR out of whatever ring it is in. The ring is reclosed. If N of XR is not in a ring, LTAKE does nothing. N of XR must not be a hen with chickens.

  PUTL ≡ N×XR→M×XR2
  PUTR ≡ N×XR→M×XR2

Put N of XR into the ring of which Mof XR2 is a member. N of XR is placed to the left (PUTL) or right (PUTR) of M of XR2. M of XR2 may be either a hen or a chicken. N of XR must not already belong to a ring.

  MOVEL ≡ N×XR→M×XR2
  MOVER ≡ N×XR→M×XR2

The Sketchpad quotes are taken from my online edition of Sutherland's thesis:

Comments

  • @AlanBlackwell Is the contrast you are trying to make here to do with uncommented, almost self-documenting “high level” code v low level code that requires elaborate explanation?

  • What mainly interests me here is the reflections on dimensionality of representation. "Code" in CCS is understood to mean linear streams of symbols, and the syntax specification of languages is uni-dimensional, describing the before and after, but not the above and below. Yet every computer screen has two dimensions. These fragments of code express the tension between the two-dimensional experience of our screen life, and the one-dimensional language modes that fail to adequately describe this. The vivid pictorial imagery of Sutherland's hens and chickens is an intriguing insight into the mental life of the man often credited with inventing the graphical user interface. My own code compensates for the lack of consideration given to natural expressions of spatial language, requiring the careful mathematical definition of relations that even a child understands.

  • @AlanBlackwell

    "Code" in CCS is understood to mean linear streams of symbols, and the syntax specification of languages is uni-dimensional, describing the before and after, but not the above and below.

    Really? I thought that the linear stream of symbols was just the convenient rendering of code. (I guess we could argue about the specific meaning of the word in the phrase "CCS", but that's presumably neither here nor there.)

    Code is the stuff that makes a machine (perhaps a meat machine) behave. Linear streams of symbols are a common rendering for code as it's easier for us to critique those, and easier to write editors for them, because we can put them on paper (originally punch cards), but the code in the mind of the programmer (and certainly in the machine) is, to your point, a complex web of machinery. One of the many reasons that Lisp is so wonderful (and probably other languages as well) is precisely that the linear stream of symbols -- the syntax -- doesn't get in your way as there pretty much is no syntax (well, not too much, as long as you have a paren balancer in your editor :-) and what syntax is there you can fix if you don't like it, and add your own. It's a meta programming language that relieves the programmer from having to think in its terms.

    Programmers almost never think of a linear stream of symbols, even if we eventually have to put it into that form, but that's just the rendering of the program, not really the program. If "code" in CCS is just a linear stream of symbols then this field is focused in the wrong place....but I don't think it is -- although it might be mis-named. Maybe it should be Critical Computing Studies and avoid folks thinking we're talking about linear streams of symbols.

  • I spent a few years coding in Lisp, so happy to discuss the way that it allows data to be structured in non-linear ways - and I think that is precisely the kind of intuition Sutherland was trying to capture. However, the memory of the von Neumann architecture is structured linearly, and compilers certainly process the input source code as a linear stream. The question of whether programmers believe they do or don't "think of a linear stream of symbols" is one that I investigated years ago with Marian Petre, in a 1999 study analysing programmers' self-reported intuitions: Mental Imagery in Program Design and Visual Programming. I guess there are plenty of people here who know more literary theory than I do - where do you go to understand whether experience of a "text" is more multidimensional than the order that you read the words? Palimpsest, on the other hand, is determinedly non-textual (and quite LISP-influenced), but with the syntax only ever two-dimensional, in terms of regions, boundaries and points.

  • @AlanBlackwell Citing the von Neumann architecture or compiler lexing confuses the "shipping container" with the content. Even in a strictly linear memory model, the existence of a pointer acts as a spatial wormhole, transforming a 1D line into a searchable manifold. If we actually experienced memory as linear, we wouldn't need addresses; we’d just stream data like a cassette tape. Furthermore, while a compiler begins with a lexer's linear stream, it immediately translates that into anAST or CFG to make sense of the logic. The linearity is a trivial artifact of our current editing tools and hardware storage, not a property of the logic itself. You own work, as well as the many versionof Boxer-like coding environmetns belie the linearity of code pooint.

    Moreover, programmers do not think in linear streams. We think in chunks, scopes, and state transitions. When we "read" code, we are rarely reading it like a novel; we are tracing dependencies, jumping to definitions, and mentally simulating a branching graph. I do this literally for hours a day evry day. I'm often reading traces and trying to trace pointers and such like, and, sure, at the end ofthe day I need to linearize my changes for the sake of being able to type them in, but this is almost always nearly trivial v reasoning about the mental model, which is an interconnected web of behaviors where "above and below" are replaced by "parent and child" or "caller and callee" and data moving around in numerous complex wyas. To define code by its 1D syntax is to mistake the map for the territory, or perhaps or more precisely, to mistake the alphabet for the architecture.

    I stuck the above, plus our conversation into Gemini looking for backing for my position. It tpointed me to "Espen Aarseth and his concept of Ergodic Literature (from the book Cybertext). Aarseth argues that certain texts—including computer code—require "non-trivial effort" to traverse, distinguishing them from linear "unicursal" texts where the path is fixed. This framework treats the program not as a sequence of words, but as a machine that the reader operates. By this definition, the "code" is the topological structure of possible paths, and the linear source file is merely one (rather limited) way to render a cross-section of that machine."

    I have no idea what that is all about, but I'll be other CCSers do!

    ps. Regarding Lisp itself, as you proabably know it's famously not just the data that is graph-structured because ... you know, the data<->program thing. :-)

  • @AlanBlackwell --

    Rather than focusing on the question of the dimensionality (1, 2, 3...), I think the things that I notice about these two code snippets when taken together is directionality and orientation.

    I think one of the things that strikes me about both of the pieces of code is that spatial metaphors are also intentional ones, so we write the code in spatial keywords as a form of documenting its intended use, then orient ourselves to it spatially in order to use it.

    Take the first example of (Axis-Aligned) Bounding Boxes in Java, a kind of code that I'm quite familiar with from many years helping to moderate the Processing forums. Code libraries like this often depend on a set of shared assumptions between all functions on what an origin is and what the coordinate system is. In particular, many 2D graphics rendering paradigms have a coordinate system with an origin; often this origin is conventionally at either the lower-left or upper-left corner of the screen. For example, in Java Processing (and Java AWT and Swing, and JavaFx) the origin is at the top-left, like a windowing system. By contrast, in OpenGL (e.g. JOGL or LWJGL) the origin is in the lower-left. There are also other possibilities, e.g. Turtle places 0,0 at the screen center, while the original Spacewar! (1962) had its Y-axis at the center of the screen but I believe its Y-axis origin was on the left side, for complicated reasons. Commonly, Java graphics snippets that are being ported from one library to another may need to be changed either because they have inverted y axes or because they store the origin of a shape (e.g. a polygon) in a different default encoding -- often the lower-left, the lower-right, or the center.

    Because the example BoundingBox.java code contains the line:

    y = destination.getBottom() - height;
    

    ...I can guess that the Y axis increases from the bottom to the top, so we are in an OpenGL-like lower-left coordinate paradigm (assuming this code even relates to a screen). Of course, I could still be wrong -- perhaps all heights are being stored as negative numbers! -- but if I take height itself as a metaphor then I'll presume it conventionally isn't negative. This, again, can get me into trouble pretty quickly if I assume certain things about how "top" and "bottom" relate to "height" but allow a value that causes the "bottom" to be higher than the "top." What the origin of a coordinate system is or a shape has far-reaching implications not only for the interpretations of ideas like "above" or "below" but also for e.g. how collision detection logic is implemented. The same is true of "left" and "right" if I allow negative widths. As someone new to this thinking goes through the process of writing such a library and encounters more and more edge cases, the practice of calling e.g. destination.getBottom() might emerge naturally as we stop considering the bottom of a box to be e.g. an integer variable (a stored y-coordinate) and start instead viewing "bottom" as a description derived from the total coordinates of the box and their relationship to its coordinate system. All of this belt-and-suspenders Java abstraction and encapsulation makes the system more robust (e.g. you can drag the upper-left corner of a bounding box over to the lower-right, and its height and collisions will still be computed correctly), but it doesn't change the fact that our expectations as a coder will still require a shared understanding of origins and axis orientations, otherwise we will be quite surprised!

    If, after spending a while in that paradigm, I then went to read this p5.js sketch:

    let y = 0;
    function setup() {
      createCanvas(300, 300);
      fill(255,0,0);
    }
    function draw() {
      background(0);
      circle(width/2, y, 50);
      y += 5; // move down
      if (y > height+25) {y = -25;}
    }
    

    ...and was told that draw() will be continuously called, then I could see easily that y begins below 0, increases until it is greater than the height of the screen, then returns to below zero and increases again. All these statements are true, yet if I wasn't familiar with p5.js, I might be surprised to see these added code comments:

    // shape sinking
    let y = 0; // start at top
    function setup() {
      createCanvas(300, 300);
      fill(255,0,0);
    }
    function draw() {
      background(0);
      circle(width/2, y, 50);
      y += 5; // move down
      describe('circle sinking');
      if (y > height+25) {y = -25;} // jump to top
    }
    

    ... because for y to increase is for the circle to sink, not rise, and for y to be greater than height is for it to move below the bottom of the screen, not above the top.

    Often the total spatial paradigm of the code and its interrelationships cannot be present in the executable source code of individual functions / modules / libraries, despite their many uses of "width" "height" "left" "right" "top" "bottom" etc. Instead the paradigm exists in the comments, in the documentation, and in the actual effects of the outputs on the target platform.

    How does this relate to "hens" and "chickens"?

    The convention of going “forward” around a ring by progressing to the right in such a representation is clear, as is the fact that putting in new chickens to the left of the hen puts them “last,”

    I think it is because we see here a similar attempt to construct a shared metaphorical orientation based on the intended use and expected outputs of the code. "Left" and "Right" here are also merely metaphors -- metaphors that are replacing other metaphors. One of the strange things about the logic of coordinate system math is that (origins aside) it is often highly symmetrical -- if you simply agreed that by convention "up" was "down" or "left" was "right" all the functions are already able to create in internally consistent set of correct outputs. If you are programming for a projector that will bounce off a mirror, this deciding that "left" means "right" may in fact be exactly what you want to do.

    Why is it now clear that a "hen" would have, on its right side, the first "chicken", and that the last chicken in a ring would be placed to its left? In the metaphor of the written page in English, writing proceeds from left-to-right, then from top-to-bottom (like Java AWT, like Processing), so "next" is intuitively "to the right." This is part of a system of ordered thinking all based in conventions. If we sat in the middle of a ring of chickens and were asked to point to "the next" we might use the preferred ordering of reading (next is to the right) which would also be the preferred motion of a clock (clockwise is right-wise). It is all very Derridean -- spatiality has pairs of terms (up/down, right/left, forward/back, next/previous) and there are marked and unmarked terms, and in certain concepts of space the preferred terms are aligned and metaphorically interchangeable (right = next = clockwise = forward). Of course, all of these alignments are arbitrary -- clockwise-ness is only "rightward" when the subject position is at the center. When viewed from the view of a chicken playing poker, play passes to the chicken's left -- that is to say, the next player on the dealer's right. We can try to ground these perspectives in a deeper nature -- for example, the narrative of the natural clockwise-ness of the clock as rooted in the natural phenomena of the sundial shadow -- but this only reveals that clocks are a natural representation of shadows in the Northern Hemisphere (as sundials move counter-clockwise in Australia).

    So the hen is "first" by the lights of English writing and northern sundials. The hen is also the first-among-equals in its ring, so it is represented within in the ring (left-right, but also "a little above" (that is to say, the first to be referenced in a ring -- first in terms of the top-bottom page-paradigm). If it the hen were instead first in an OPENGL lower-left axis coordinate paradigm, then our hen would instead be first by being "a little below."

    It is also clear why this wouldn't be immediately clear in another cultural context or metaphorical frame, where the "next" letter or word or glyph in writing should perhaps be down, and thus our metaphor should be stackable objects, like a stack of plates oriented by gravity (as chickens may or may not congregate in rings, but they definitely do not congregate in vertical stacks). And a culture with a tradition of boustrophedon writing "as the ox plows" back and forth might find the entire idea of associating "next"-ness with either left or right to be a pointless exercise.

    I suppose my conclusion is that reading BoundingBox.java is an invitation to join an existing spatial paradigm (which must be deduced) and "HUMAN REPRESENTATION OF RING STRUCTURE" is an attempt to invent a spatial paradigm which will be culturally appropriate and intuitive -- and that both must necessarily rest on a huge collection of cultural assumptions about what space is and how it works.

Sign In or Register to comment.