Working Draft
Indra’s Net ƒ
The contents and significance of the Indra’s Net HyperCard stack is discussed in the corresponding expository chapter for this notebook. Here, we get on with exploring and reengineering selected dynamic pieces within the collection.
The focal hologogramic transformation within Indra’s Net was the acrostic, which, at this time, I categorized in a number of ways. The basic form was a “strict abecedarian or [head-]acrostic,” the description of which is given in the corresponding exposition. This hologogram will be outline here along with the “free abecedarian or internal acrostic,” which was known to other practitioners as a mesostic. ‘Under It All’ implemented the strict form and ‘Under It All II’ displayed the free form, then considered by me as an advance in terms of poetics. The supply text for both pieces is the same:
We this text into a JavaScript string literal. In HyperCard, this text retained line breaks that are also respected here, in the JavaScript:
const supplyTextString = supplyText.innerText
display(supplyTextString);The following script in HyperTalk – defFrAcros for ‘define free acrostic’ – in fact allowed both strict and free ‘definitions’ to be created for words being transformed by the acrostic, depending on whether the string "head" (rather than anything else) was passed to the acrosType parameter of the function.
on defFrAcros acrosType
put item 1 of fld label into textName
put 1 into xword
put "abcdefghijklmnopqrstuvwxyz" into alpha
put word xword of field 1 into testw
set numberFormat to "0000"
put the number of words in fld text into totWords
lock screen
repeat with i = 1 to totWords
put word i of fld text into testw
put i into wordNo
if acrosType is "head" then
put 1 into defAllChars
else
put the length of testw into defAllChars
end if
repeat with c = 1 to defAllChars
put char c of testw into testc
if alpha contains testc then
push card
go cd textName & ",def," & testc
if the result is not empty then
go card "blank,def"
doMenu "New Card"
put textName & ",def," & testc into fld label
set the cantDelete of this cd to false
end if
-- next condition would not have regard to frequencies
-- if testw is not in field defStuff then
put testw & " " after field defStuff
put wordNo & " " after field defNos
-- end if
pop card
end if
end repeat
put wordNo & "/" & totWords
end repeat
unlock screen
end defFrAcros
This script works through all the words in the supply text. For each word, if acrosType is "head" it puts 1 – for the initial character of the word – into defAllChars, the number of characters in the word that will be processed; otherwise it processes all the characters in the word and defAllChars will contain the number of characters in the word. For each defAllChars of the word, the script checks to see if there exists a corresponding card – named for one of the twenty-six letters of the lowercase alphabet. If there isn’t such a card it creates one and adds an instance of the word to a field on the card; if there is such a card it simply adds a word instance to the field. This creates what could be otherwise represented by a JavaScript Map of word occurences indexed by letters of the alphabet. If the type of acrostic is head-acrostic, the a card, for example, will contain a list of all the word occurrences beginning with a; if not, it will contain a list of all word occurrences that contain a letter a.
function acrosticsMap(supplyText, acrosticType) {
let acrostics = new Map(); // create a Map
let words = supplyText.split(/\s/); // split string literal supply text into words
for (let i = 0; i < words.length; i++) {
// iterate through the words
// get number of characters to process - only the first one for head-acrostic
let charactersToProcess = "head" === acrosticType ? 1 : words[i].length;
for (let j = 0; j < charactersToProcess; j++) {
// iterate through the characters
let character = words[i][j]; // get the current character
if (acrostics.has(character)) {
// if we already have an entry for the character
acrostics.get(character).push(words[i]); // add the word-occurrence to the array for the character
} else {
// we don't yet have an entry for the character, so
acrostics.set(character, [words[i]]); // create an entry with an array containing the word-occurrence
} // end of innner j loop
}
}
return acrostics;
}And then, we can evoke it in two forms, one for head-acrostics and one for a free-acrostic or mesostic:
const textTransMapHead = acrosticsMap(supplyTextString, "head");
display(textTransMapHead);const textTransMapFree = acrosticsMap(supplyTextString);
display(textTransMapFree);Now we code a ‘player’ that performs the letteral hologogram stuctures implicit in both the head-acrostic and free or mesostic Maps. In the HyperCard version, after clicking the relevant item of the stack’s table of contents card, you were sent to a player card that was appropriate for your choice. A text was generated and displayed by a HyperTalk script. In the head-acrostic version, for each letter of the supply text, random words from the same text that begin with the letter are displayed in succession. In the mesostic version, random words that include each letter are displayed.
First, a player function in Javascript that does the same thing.
async function acrosticPlayer (supplyText, supplyTextMap, displayId) {
let words = supplyText.split(/\s/); // put the supply text's words into an array
let display = document.getElementById(displayId); // this is our display area
// get a reference to the element where the word currently generating the acrostic is displayed:
let displayMonitor = document.getElementById(displayId + "Mntr");
for (let i = 0; i < words.length; i++) {
// this loops for the number of words in the supply text
// and then allows the function to return (as the original did)
let currentWord = words[i];
let screenWords = [];
let j; // we need j outside the loop, so declare it here
for (j = 0; j < currentWord.length; j++) {
let character = currentWord[j];
// add word sastifying the acrostic rule to the screenWords that will be displayed:
if (supplyTextMap.has(character)) {
let possibleWords = supplyTextMap.get(character);
screenWords.push(possibleWords[randomInteger(possibleWords.length)]);
}
}
fadeInByWord([currentWord], displayMonitor, 800); // fade in the supply text word in lower register
fadeInByWord(screenWords, display, 800); // fade in the words of the acrostic, one by one
// time is needed to complete this animation
await Promises.delay(j * 800 + 800);
// gracefully fade out the acrostic words
display.style.color = "ivory";
displayMonitor.style.color = "ivory";
// time is needed for the color transition
await Promises.delay(3000);
// clear and reset the text color of the display area
display.innerHTML = "";
display.style.color = "black";
displayMonitor.innerHTML = "";
displayMonitor.style.color = "black";
}
return "The acrostic player has finished playing. Refresh page to restart.";
}Here is a display area for the head-acrostic version:
And code that evokes the player for this display:
acrosticPlayer(supplyTextString, textTransMapHead, "uiaHead");And here is another display for the mesostic version:
With a corresponding invocation:
acrosticPlayer(supplyTextString, textTransMapFree, "uiaMesostic")The playText HyperTalk script from the original stack is given below for comparison. Details of how the display was animated, however, will be found in the scripts attached to specific HyperCards where specific contents items were actually played. The transWord and showWord scripts of these cards did the work.
Lastly, in these notes, we will explore the implementation of what I then called ‘collocational constraints’ as applied to the mesostic form of ‘Under It All II.’ These constraints were implemented as a simple bigram Markov model, and such models will be discussed in more detail in Collocations ƒ. The reengineered implementation is placed here, to allow readers to compared the juxtapose versions: the generated text-to-be-read of the constrained version with that of the earlier unconstrained version (above). Also, in a further development that was introduced by Moods & Conjuctions, those characters transformed in the mesostic form are highlighted in bold as the text is generated. The HyperTalk scripts that realized these later features are also quoted for comparison with earlier scripts, and with their JavaScript reengineering.
So, first, a new display area where we will animate ‘Under It All II’ as a mesostic with collocational constraints:
We will need to make a Map of word occurrences in the supply text. These are explained in Collocations ƒ from which we will also import the function that constructs such a Map, invoking it immediately following the import.
import { occurrencesMap } from "observable:@shadoof/inapm2_collocations_f"const uiaOccurrences = occurrencesMap(supplyTextString);
display(uiaOccurrences);And we’ll need a new player function for this collocationally constrained version, one to which we can pass our uiaOccurrences:
async function acroCollocPlayer (
supplyText,
supplyTextMap,
occurrencesMap,
displayId
) {
let words = supplyText.split(/\s/); // put the supply texts words into an array
let display = document.getElementById(displayId); // this is our display area
// get a reference to the element where the word currently generating the acrostic is displayed:
let displayMonitor = document.getElementById(displayId + "Mntr");
let previousScreenWord = words[words.length - 1]; // initially put last word into previousScreenWord
for (let i = 0; i < words.length; i++) {
// this loops for the number of words in the supply text
// and then allows the function to return (as the original did)
let currentWord = words[i];
let screenWords = [];
let j; // we need j outside the loop, so declare it here
for (j = 0; j < currentWord.length; j++) {
let character = currentWord[j];
// we are going to try and find an acrostic-satisfying collocation
let foundColloc = false;
// add a word sastifying the acrostic rule to the display
if (supplyTextMap.has(character)) {
let possibleWords = supplyTextMap.get(character);
// if any of these possible words collocates with the previousWord
// then pick it, putting true into foundColloc
let currentScreenWord;
for (currentScreenWord of possibleWords) {
let occs;
try {
occs = occurrencesMap.get(currentScreenWord);
} catch (e) {
console.log("Word not found in occurrences Map."); // should not happen
}
// looping through the occurrences of current occ and previous prevOcc
for (const occ of occs) {
for (const prevOcc of occurrencesMap.get(previousScreenWord)) {
// if the occurrence numbers differ by 1, this is a collocation
if (occ - prevOcc == 1) {
foundColloc = true;
// console.log(
// "found:",
// `${previousScreenWord} ${currentScreenWord}`
// ); // DEBUG
break; // from prevOccs
}
}
if (foundColloc) break; // from occs
}
if (foundColloc) break; // from posibleWords
} // we've looped through possibleWords
// if no foundColloc pick a random acrostically qualifying word
if (!foundColloc)
currentScreenWord =
possibleWords[randomInteger(possibleWords.length)];
previousScreenWord = currentScreenWord;
// embolden the acrostic letter
let charOffset = currentScreenWord.indexOf(character);
currentScreenWord =
currentScreenWord.substring(0, charOffset) +
"<b>" +
character +
"</b>" +
currentScreenWord.substring(charOffset + 1);
screenWords.push(currentScreenWord);
} // end characters loop
} // end words loop
fadeInByWord([currentWord], displayMonitor, 800);
fadeInByWord(screenWords, display, 800); // fade in the screenWords, one by one
// time is needed to complete this animation
await Promises.delay(j * 800 + 800);
// gracefully fade out the screen words
display.style.color = "ivory";
displayMonitor.style.color = "ivory";
// time is needed for the color transition
await Promises.delay(3000);
// clear and reset the text color of the display area
display.innerHTML = "";
display.style.color = "black";
displayMonitor.innerHTML = "";
displayMonitor.style.color = "black";
}
return "The acrostic player has finished playing. Refresh page to restart.";
}Finally, the animation is started by invoking this function on page refresh:
const playMesoColloc = acroCollocPlayer(
supplyTextString,
textTransMapFree,
uiaOccurrences,
"uiaMesoColloc"
)1. placeholder note.
This cell is styling CSS for the display elements.
<p><em>This cell is styling CSS for the display elements.</em></p>
<style>
.display {
height: 30vw;
width: 45vw;
margin-left: 3vw;
font-size: 3vw;
background-color: ivory;
padding: 1vw;
overflow: hidden;
transition: color 2.5s ease-in-out;
-o-transition: color 2.5s ease-in-out;
-ms-transition: color 2.5s ease-in-out;
-moz-transition: color 2.5s ease-in-out;
-webkit-transition: color 2.5s ease-in-out;
}
.display b {
font-weight: 550;
}
.display.monitor {
height: 3vw;
font-size: 2.5vw;
}
</style>Selected Raw HyperTalk Scripts
The original HyperCard stacks had, typically, five backgrounds:
- PrelimPlus : Pages or screens readable in a conventional manner by book-like navigation.
- cardSet : A number of cards shared this background with particular named cards used for one another readable animation.
- texts : With versions of the supply text formatted for preprocessing.
- defs : Basically used as a relatively simple data structure. Cards with this background were created and named dynamically during preprocessing, storing information about words in the texts.
from the Stack script of Indra’s Net
on playText
global ¬
textname,¬
wherePlay,¬
numOWords,¬
newWord, ¬
i, ¬
showBase, ¬
trans, ¬
playing, ¬
interrupt,veFactor,ve
put true into playing
set cursor to none
hide menuBar
put the number of words in fld text of cd textName & ",text" ¬
into numOWords
put empty into cd fld baseFld of cd wherePlay
hide cd fld baseFld of cd wherePlay
put empty into cd fld playFld of cd wherePlay
go cd wherePlay
-- put 0 into i
put false into interrupt
repeat until i is numOWords
add 1 to i
if the mouse is down and the shiftKey is down then exit repeat
put word i of fld text of cd textName & ",text" ¬
into newWord
if showBase then
put newWord into cd fld baseFld
end if
send transWord to cd
if interrupt then
exit repeat
end if
if showBase then
lock screen
hide cd fld baseFld
do "unlock screen with" && ve && veFactor
end if
end repeat
show cd fld baseFld of cd wherePlay
end playText
on seekWord xWord
global ¬
textname,¬
wherePlay,¬
numOWords,¬
newWord, ¬
i, ¬
showBase, ¬
trans, ¬
playing, ¬
interrupt
put xWord into trans
put false into wordFound
repeat until wordFound
show cd fld baseFld
put word i of fld text of cd textName & ",text" into newWord
put newWord into cd fld baseFld
if newWord is trans then
put true into wordFound
subtract 1 from i
else
add 1 to i
if i > numOWords then put 1 into i
end if
end repeat
put false into interrupt
playText
end seekWord
from the script of the Card acrostic1 in Indra’s Net
on transWord
global ¬
textname,¬
wherePlay,¬
numOWords,¬
newWord, ¬
i, ¬
showBase, ¬
trans, ¬
interrupt,veFactor,sFactor,ve
put empty into cd fld playFld
put length(newWord) into wordLen
repeat with j = 1 to wordLen
-- taylored transformation routine goes here
put char j of newWord into newChar
if not (newChar < "a") or (newChar > "z") then
put textname & ",def," & newChar into defCard
put the number of words in fld defStuff of cd defCard ¬
into numODef
put trans into lastTrans
put word random(numODef) of fld defStuff of cd defCard ¬
into trans
if (trans = lastTrans) and (numODef > 2) then
repeat until trans <> lastTrans
put word random(numODef) of fld defStuff of cd defCard ¬
into trans
end repeat
end if
-- now show the transformation
if the mouse is down then
put lastTrans into trans
put trans into cd fld baseFld
put true into interrupt
exit repeat
end if
showTrans
wait sFactor
end if
end repeat
if not interrupt then
if i = numOWords then wait 5 seconds else wait sFactor
lock screen
put empty into cd fld baseFld
put empty into cd fld playFld
do "unlock screen with" && ve && veFactor
end if
end transWord
on showTrans
global showBase,trans,veFactor,ve
-- showing is also taylored for each playing card
lock screen
put trans & " " after cd fld playFld
if showBase then show cd fld baseFld
do "unlock screen with" && ve && veFactor
end showTrans
updated version of scripts from acrostic1 in Moods & Conjunctions
(as used in Moods & Conjunctions)
on transWord
global ¬
textname,¬
wherePlay,¬
numOWords,¬
newWord, ¬
i, ¬
showBase, ¬
trans, ¬
lastTrans, ¬
collocRef, ¬
lastCollocRef, ¬
interrupt, ¬
pickedColloc,veFactor,withColloc,defName,j,wordlen,sFactor,newChar,ve
put empty into cd fld playFld
put length(newWord) into wordLen
repeat with j = 1 to wordLen
-- taylored transformation routine goes here
put char j of newWord into newChar
if not (newChar < "a") or (newChar > "z") then
put defname & ",def," & newChar into defCard
if there is a cd defName & ",Occs," & lastTrans then
put (pickOcc(lastTrans)+1) into collocRef
else
put (collocRef+1) into collocRef
end if
put false into pickedColloc
put offset(collocRef,fld defNos of cd defCard) into collocOff
if (collocOff <> 0) and withColloc then
put true into pickedColloc
put (((collocOff-1) div 5) + 1) into collocPicked
put word collocPicked of fld defStuff of ¬
card defCard into trans
put word collocPicked of fld defNos of ¬
card defCard into collocRef
else
put the number of words in fld defStuff of cd defCard ¬
into numODef
put random(numODef) into randPicked
put word randPicked of fld defStuff of cd defCard ¬
into trans
put word randPicked of fld defNos of cd defCard ¬
into collocRef
if (trans = lastTrans) and (numODef > 2) then
repeat until trans <> lastTrans
put word random(numODef) of fld defStuff of cd defCard ¬
into trans
end repeat
end if -- there was or wasn't a valid collocation
end if
-- now show the transformation
if the mouse is down then
put lastTrans into trans
put trans into cd fld baseFld
put true into interrupt
exit repeat
end if
showTrans
wait sFactor
put trans into lastTrans
put collocRef into lastCollocRef
end if
end repeat
if not interrupt then
if i = numOWords then wait 5 seconds else wait 90
lock screen
put empty into cd fld baseFld
put empty into cd fld playFld
do" unlock screen with " & ve && veFactor
end if
end transWord
on showTrans
global showBase,trans,pickedColloc,veFactor,j,wordlen,newChar,textName,ve
-- showing is also taylored for each playing card
lock screen
-- if pickedColloc then put "*" after cd fld playFld
put trans & " " after cd fld playFld
set the textStyle of char ¬
offset(newChar,the last word of cd fld playFld) ¬
of the last word of cd fld playFld to bold
if collocRef & "," is in fld endWords of cd textName & ",text" then
put return after cd fld playFld
end if
if showBase then show cd fld baseFld
do "unlock screen with " & ve && veFactor
if (j is wordlen) ¬
and ("," & trans & "," is in ¬
",fallen,resolve,rooms,beneath,return,white,themselves,still,forms,pursue,techniques,demands,drawn,gales,lights,") ¬
then
push cd
lock screen
put empty into cd fld playFld
put empty into cd fld baseFld
do "go cd ill0" & random(4)
do "unlock screen with dissolve very slow"
wait 180
lock screen
pop cd
do "unlock screen with dissolve very slow"
end if
end showTrans
import { css, fadeInByWord, randomInteger } from "observable:@shadoof/inapm0_shared"