--- title: "Fractal Theatre" id: 71042 type: "computer_media" slug: "fractal-theatre" url: "http://localhost/computer_media/fractal-theatre/" markdown_url: "http://localhost/computer_media/fractal-theatre.md" published_at: "2026-08-26T08:01:14+00:00" modified_at: "2026-08-26T08:02:31+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/fractal-theatre.png" alt: "Fractal Theatre screen" excerpt: "Store and play back a \"movie\" of complex fractal curves by compressing plotted screens into coordinate pairs inside a single string array." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Michael Leidel" slug: "michael-leidel" taxonomy: "indiv" url: "http://localhost/indiv/michael-leidel/" genre: - name: "Mathematics" slug: "mathematics" taxonomy: "genre" url: "http://localhost/type/mathematics/" media_type: "Program" programmers: - name: "Michael Leidel" slug: "michael-leidel" taxonomy: "indiv" url: "http://localhost/indiv/michael-leidel/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Fractal%20Theatre%20%28198x%29%28Leidel%2C%20Michael%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/fractal-theatre.png" alt: "Fractal Theatre screen" media_type_tags: "Mathematics" --- # Fractal Theatre Fractal Theatre is an interactive fractal curve plotter and “movie” recorder that generates Julia set-like curves using iterated complex-number arithmetic on the TS2068. The program computes a complex square root and inverse transformation in BASIC subroutines (lines 2050–2170), iterating a lambda-parameterized mapping to produce attractor plots one pixel at a time. Completed screens are compressed into a string array `f$()` by scanning each character cell with `ATTR` and recording only ink-pixel coordinates as CHR$ pairs, allowing multiple high-resolution frames to be stored in a single 10,000-element array. Stored frames can be played back in rapid succession as a “movie,” optionally pausing between frames, and the file can be saved and loaded as a DATA array to tape with optional VERIFY. Utility options allow color customization, toggling a pause flag between frames, and saving the program itself with auto-run. *** ## Program Analysis ### Program Structure The program is organized as a menu-driven application with a computed `GO TO` dispatcher at line 25. The main menu (line 15) offers eight options; selecting one jumps to a block of subroutines grouped by thousands in line number. The variable `rst` is initialized to `10` and acts as a “restart” address, so `GO TO rst` returns to the main menu without retyping the line number—a common idiom for keeping variables intact across re-entries. | Line range | Purpose | | --- | --- | | 1–25 | Initialization and main menu dispatcher | | 100 | File status display (calls line 6000) | | 1001–1025 | Clear/initialize movie file array | | 1500 | Generic “press ENTER to continue” pause | | 2001–2535 | Plot next frame: iteration, plotting, frame storage | | 2600–2630 | Store ink pixels from one character cell | | 2700–2720 | Movie file overflow handler | | 3001–3045 | Play movie from array | | 4001–4550 | Save and verify movie file to tape | | 5001–5080 | Load movie file from tape | | 6001–6020 | Display file status / statistics | | 8001–8085 | Documentation / “Read Me” text | | 9001–9998 | Utility menu: colors, save program, quit, pause flag | ### Computed GO TO Dispatcher Lines 25 and 9015 use an arithmetic expression as the argument to `GO TO`. Each term is of the form `(line_number AND condition)`, where the Boolean result of the condition is 1 (true) or 0 (false), so exactly one term is non-zero. This neatly selects the target line without a chain of `IF`/`GO TO` statements and is a well-known Spectrum/TS2068 BASIC optimization: `GO TO (0100 AND i=1)+(1000 AND i=2)+...` The final term `(0020 AND (i<1 OR i>8))` loops back to line 20 (re-prompt) on invalid input. ### Complex Arithmetic in BASIC The fractal iteration is implemented as three subroutines operating on real/imaginary pairs stored in plain variables: - **Lines 2050–2080**: Complex square root of `x + jy` using the formula `√((|z|±x)/2)`, with sign correction for negative imaginary part. - **Lines 2100–2120**: Computes `4 / (lx + j·ly)` (the reciprocal scaled by 4), modifying `lx` and `ly` in place. - **Lines 2150–2170**: Complex multiplication of `(x+jy)` by `(lx+jly)`. The main iteration subroutine (lines 2200–2235) applies these in sequence: multiply by lambda, subtract from 1, take the square root, randomly negate to choose one of the two roots (the “random iteration algorithm” / chaos game approach), then compute both the positive and negative root positions simultaneously for efficiency. Ten warm-up iterations (line 2025) are performed before plotting begins. ### Screen Compression into String Array Rather than saving entire bitmapped screens, the program compresses plotted pixels into coordinate pairs. Line 1005 allocates `f$()` as a 10,000-element string array (each element one character). The storage process at lines 2501–2630 works as follows: 1. Iterate over all 32 columns and 20 rows of character cells. 2. Use `ATTR (l,c)` to detect cells containing ink pixels (comparing against a computed attribute value `attest`). 3. For each such cell, use `POINT (x,y)` to test individual pixels and store lit ones as two consecutive CHR$ values (x and y coordinate). 4. Frame boundaries are marked with two consecutive `CHR$ 255` sentinels. The current write pointer `cp` is tracked throughout. On save, the value of `cp` is packed into the first two array elements as a little-endian 16-bit integer: `f$(1)=CHR$(cp MOD 256)`, `f$(2)=CHR$(INT(cp/256))`. On load, `cp` is reconstructed with `CODE(f$(1))+256*CODE(f$(2))`. ### ON ERR Usage `ON ERR GO TO` is used defensively in several places: - Lines 2300/2310: Catch out-of-screen `PLOT` errors silently and continue. - Lines 4515/4530 and 5020/5035: Catch tape verify/load errors and branch to an error message without crashing. - Line 9235: Catch program verify errors. Each guarded block is followed by `ON ERR RESET` to restore normal error handling. ### Tape File Naming with “@” Prefix Lines 4520, 9240, and 9245 check whether the filename begins with `"@"` and, if so, construct the actual filename as `n$(1)+n$(4 TO )`—stripping the middle characters. This appears to be a convention for addressing a specific tape drive or device prefix, where the `@` and following characters encode a device specifier and the real name begins at position 4. ### Color Prompt String k$ Line 8 initializes `k$` as `CHR$ 16 + CHR$ 9`, which is the INK control sequence for color 9 (transparent/contrast ink on TS2068). This is prepended to all `INPUT` prompts via `INPUT (k$);"prompt";variable`, ensuring input prompts appear in a consistent color regardless of the current paper/ink settings. ### Movie Playback Playback (lines 3001–3045) iterates through `f$()` from index 3 to `cp-4` in steps of 2, reading each coordinate pair with `CODE(f$(f))` and `CODE(f$(f+1))` and calling `PLOT fx,fy`. When a double-`CHR$ 255` sentinel is encountered, the screen is cleared (and optionally paused if `pflag` is set) to display the next frame. The pause flag is toggled from the utility menu. ### Notable Anomalies - Line 2400 is the target of `GO TO 2400` at line 2045, but the actual frame-end code starts at line 2401/2405—the program falls through correctly since line 2400 does not exist and execution continues at the next available line (2401), which is a `REM`, then 2405. - Line 9000 is targeted by several `GO TO 9000` statements in the utility menu, but the utility menu block starts at line 9001. This is the same fall-through-to-next-line technique. - Similarly, `GO TO 9200` (line 9280) and `GO TO 1000` (line 5080) target non-existent lines, relying on execution falling to the next present line—a deliberate space-saving technique. - Line 4015 prints variable `N$` (uppercase) while line 4010 assigns `n$` (lowercase); on this platform these are the same variable, so there is no bug. - The `BEEP` calls in the storage loop (lines 2520 and 2620) provide audio feedback during the slow pixel-scanning process—a short low tone per column and a higher tone per stored pixel. ## Source Code ``` 1 REM FRACTAL THEATRE7/22/85Michael Leidel 8 LET pflag=0:LET mink=7:LET rst=10:DIM i$(1):LET cx=100:LET cy=88:LET sc=1:LET mbord=5:LET MPap=0:LET k$= CHR$ 16+ CHR$ 9 10 BORDER 5:PAPER 7:INK 1:CLS 15 PRINT " WELCOME TO FRACTAL THEATRE..."'''' TAB 6;"1 - FILE STATUS"'' TAB 6;"2 - CLEAR MOVIE FILE"'' TAB 6;"3 - PLOT NEXT FRAME"'' TAB 6;"4 - PLAY MOVIE"'' TAB 6;"5 - SAVE MOVIE DATA"'' TAB 6;"6 - LOAD MOVIE DATA"'' TAB 6;"7 - READ ME!!"'' TAB 6;"8 - UTILITY MENU" 20 INPUT (k$);"select an item: ";i 25 GO TO (0100 AND i=1)+(1000 AND i=2)+(2000 AND i=3)+(3000 AND i=4)+(4000 AND i=5)+(5000 AND i=6)+(8000 AND i=7)+(9000 AND i=8)+(0020 AND (i<1 OR i>8)) 100 GO SUB 6000:GO TO rst 1001 REM CLEAR movie file 1005 LET mp=10000:DIM f$(mp) 1010 LET f$(1)= CHR$ 255:LET f$(2)= CHR$ 255:LET cp=3 1015 GO SUB 6000 1020 LET overflow=0 1025 GO TO rst 1500 INPUT (k$);"enter to CONTINUE ";i$:RETURN 2001 REM PLOT NEXT frame 2010 LET x=.50001:LET y=0 2015 BORDER mbord:PAPER MPap:INK MPap:CLS 2020 GO SUB 2250 2025 FOR i=1 TO 10:GO SUB 2200:NEXT i 2030 GO SUB 2295 2035 GO SUB 2200 2040 IF INKEY$ <>"s" THEN GO TO 2030 2045 GO TO 2400 2050 REM square root of (x+jy) 2055 LET t=y 2060 LET s= SQR (x*x+y*y) 2065 LET y= SQR ((-x+s)/2) 2070 LET x= SQR ((+x+s)/2) 2075 IF t<0 THEN LET x=-x 2080 RETURN 2100 REM four over lx+jly 2105 LET s=lx*lx+ly*ly 2110 LET lx=+4*lx/s 2115 LET ly=-4*ly/s 2120 RETURN 2150 REM x+jy times lx+jly 2155 LET tx=x:LET ty=y 2160 LET x=tx*lx-ty*ly 2165 LET y=tx*ly+ty*lx 2170 RETURN 2200 REM function of x+jy 2205 GO SUB 2150:REM * lambda 2210 LET x=1-x 2215 GO SUB 2050:REM take root 2220 IF RND <=.5 THEN LET x=-x:LET y=-y:REM choose root to plot 2222 LET xn=-x:LET yn=-y 2225 LET x=1-x:LET xn=1-xn 2230 LET x=x/2:LET y=y/2:LET xn=xn/2:LET yn=yn/2 2235 RETURN 2250 REM get values 2255 CLS 2260 INPUT (k$);"enter lambda (x,y): ";lx,ly 2265 INPUT (k$);"change scale? ";i$:IF i$="y" THEN INPUT (k$);"enter scale:";sc 2270 INPUT (k$);"change x offset? ";i$:IF i$="y" THEN INPUT (k$);"enter x offset:";cx 2272 INPUT (k$);"change y offset? ";i$:IF i$="y" THEN INPUT (k$);"enter y offset:";cy 2275 PRINT k$;"lx=";lx;" ly=";ly;" sc=";sc;" cx=";cx;" cy=";cy; AT 21,0;"press ""s"" to STOP " 2280 GO SUB 2100 2285 LET scale=2*cx/sc 2290 RETURN 2295 REM plot x,y 2300 ON ERR GO TO 2310 2305 PLOT INK mink;scale*(x-.05)+cx,cy-scale*y:PLOT INK mink;scale*(xn-.05)+cx,cy-scale*yn 2310 ON ERR RESET 2315 RETURN 2401 REM end of frame 2405 INPUT (k$);"request or continue: ";i$ 2410 IF i$="p" THEN COPY :GO TO 2405 2415 IF i$="f" THEN GO SUB 2500:GO TO 2405 2416 IF i$="s" THEN INPUT (k$);"enter file name: ";f$:SAVE f$ SCREEN$ :GO TO 2405 2417 GO SUB 6000 2420 GO TO rst 2501 REM store movie frame 2503 IF overflow THEN GO SUB 2700:GO TO 2530 2505 LET attest=(MPap*8)+7 2507 FOR c=0 TO 31 2510 FOR l=1 TO 20 2515 IF ATTR (l,c)=attest THEN GO SUB 2600:IF overflow THEN GO TO 2530 2520 NEXT l:BEEP .1,0:NEXT c 2525 LET f$(cp)= CHR$ 255:LET cp=cp+1:LET f$(cp)= CHR$ 255:LET cp=cp+1 2535 RETURN 2601 REM store points 2605 LET cc=c*8:LET ll=175-(l*8) 2610 FOR x=cc TO cc+7 2615 FOR y=ll TO ll-7 STEP -1 2620 IF POINT (x,y) THEN LET f$(cp)= CHR$ x:LET cp=cp+1:LET f$(cp)= CHR$ y:LET cp=cp+1:BEEP .05,12:IF cp >=mp-2 THEN GO SUB 2700:RETURN 2625 NEXT y:NEXT x 2630 RETURN 2701 REM movie file overflow 2705 PRINT INK 2; AT 21,0;"** MOVIE FILE OVERFLOW **" 2710 GO SUB 1500 2715 LET overflow= NOT overflow 2720 RETURN 3001 REM play movie 3005 BORDER mbord:PAPER MPap:INK mink:CLS 3010 FOR f=3 TO cp-4 STEP 2 3015 LET fx= CODE (f$(f)):LET fy= CODE (f$(f+1)) 3020 IF fx=255 AND fy=255 AND pflag THEN PAUSE 60000:CLS :GO TO 3030 3021 IF fx=255 AND fy=255 AND NOT pflag THEN CLS :GO TO 3030 3025 PLOT fx,fy 3030 NEXT f 3035 INPUT (k$);"play it again? ";i$ 3040 IF i$="y" THEN GO TO 3005 3045 GO TO rst 4001 REM SAVE movie file 4005 CLS 4010 INPUT (k$);"enter file name: ";n$ 4015 CLS :PRINT AT 8,0; INK 1;"** SAVING FILE ";N$;" ** " 4017 LET f$(1)= CHR$ (cp-256* INT (cp/256)):LET f$(2)= CHR$ (INT (cp/256)) 4020 SAVE /N$ DATA F$() 4025 CLS :PRINT AT 8,0; INK 4;"** FILE ";n$;" IS SAVED ** " 4030 INPUT (k$);"VERIFY file? ";i$ 4035 IF i$="y" THEN GO SUB 4500 4040 INPUT (k$);"SAVE file again? ";i$ 4045 IF i$="y" THEN GO TO 4015 4047 LET f$(1)= CHR$ 255:LET f$(2)= CHR$ 255 4050 GO TO rst 4501 REM VERIFY saved file 4505 CLS 4510 PRINT INK 0; AT 8,0; INK 1;"** VERIFYING FILE ";N$;" ** " 4515 ON ERR GO TO 4540 4520 IF n$(1)="@" THEN VERIFY n$(1)+n$(4 TO ) DATA f$() 4521 IF n$(1) <>"@" THEN VERIFY n$ DATA f$() 4525 CLS :PRINT AT 8,4; INK 4;"** SAVED FILE IS O.K. **" 4530 ON ERR RESET 4535 RETURN 4540 CLS :PRINT AT 8,4; INK 2;"** SAVED FILE IS BAD **" 4545 PAUSE 5:ON ERR RESET 4550 RETURN 5001 REM LOAD movie file 5005 CLS 5010 INPUT (k$);"enter file name: ";n$ 5015 PRINT INK 0; AT 8,6; INK 1;"** LOADING FILE ";N$;" **" 5020 ON ERR GO TO 5060 5025 LOAD /N$ DATA F$() 5030 CLS :PRINT AT 8,0; INK 4;"** FILE ";n$;" IS LOADED ** " 5035 ON ERR RESET 5040 GO SUB 1500 5045 LET cp= CODE (f$(1))+256* CODE (f$(2)) 5050 LET f$(1)= CHR$ 255:LET f$(2)= CHR$ 255 5055 GO SUB 6000:GO TO rst 5060 CLS :PRINT AT 8,6; INK 2;"** FILE LOAD IS BAD **" 5065 PAUSE 5:ON ERR RESET 5070 INPUT (k$);"try load again? ";i$ 5075 IF i$="y" THEN GO TO 5005 5080 GO TO 1000 6001 REM display file size 6005 BORDER 5:PAPER 7:INK 2:CLS 6010 PRINT AT 8,0;"MAX MOVIE SIZE = ";mp;" POINTS"; AT 10,2;"CURRENT SIZE = ";cp-1;" POINTS"; AT 14,8;"default scale: ";sc; AT 15,5;"default x offset: ";cx; AT 16,5;"default y offset: ";cy 6015 IF cp >=mp-2 THEN PRINT INK 2; AT 12,4;"** MOVIE FILE IS FULL **" 6020 GO TO 1500 8001 REM documentation 8005 GO SUB 8500 8010 PRINT "This program begged to be writ- ten one day after a leisurely stroll thru FRACTAL GARDEN (withwhich you may by now be familiar). I was in the Garden, play- ing at one of my favorite gra- phic pastimes; that being the plotting of various complex curves. I was varying factors in the complex equations and watching the resultant effect on the curve. It occured to me that I was getting tired of waiting so long for each curve to plot.":GO SUB 1500 8015 GO SUB 8500 8020 PRINT "Besides that, I had to collect printouts of each curve in order to compare the effects of changing various values. I decided that it would be nice to be able to store each curve in memory so that I could view aset of curves in rapid succes- sion on-screen. Ergo this prog-ram, which builds a ""movie"" file of plotted screens.",," The program essentially scans a plotted screen and converts the X-Y coordinates of each ink pixel to a CHR$. The CHR$'s are stored in the string array f$.":GO SUB 1500 8025 GO SUB 8500 8030 PRINT " Once one screen is stored, another one can be plotted and then stored in f$. The contents of the ""movie"" file are played back by simply scanning f$ and converting the CHR$s back to integer X-Y coordinates for PLOTting. A flag can be set via the Utility menu which will cause the program to PAUSE after each screen."," OK, so it still takes a while to generate each original screen. But now you just let the ol' 2068 plot each curve while you do something else (wash the car, paint the house, etc...).":GO SUB 1500 8035 GO SUB 8500 8040 PRINT " When you think the curve looks good, ""snapshot"" the screen and fire off another. After you've stored away a number of screens, you can play back the ""movie"" of your favorite curves MUCH faster than it took to plot them in the first place. Check out the movie file which follows this program on the tape, its a demo of some of my favorites. Just select Main Menu item #6 and load movie file ""fmdemo"".":GO SUB 1500 8045 GO SUB 8500 8050 PRINT " So...maybe I'm weird and you don't get too excited by complex curves. Check out the program anyway. It's a halfway decent way to economically store a number of hi-res (monochrome) screens, if they happen to have a low ink-to-paper pixel ratio (especially since it's only BASIC). You may want to use the program design for your own application.":GO SUB 1500 8055 GO SUB 8500 8060 PRINT " NOTES ON PROGRAM OPERATION:",,," - if the program stops for any reason, use ""GOTO restart"" in order to keep the variables intact",," - all prompts which require a yes/no answer will interpret ""y"" as yes and all other responses as no.",,,," - When the program is first LOADed, be sure to clear the movie file before starting (menu option #2) to avoid the ""variable not found"" error condition":GO SUB 1500 8065 GO SUB 8500 8070 PRINT " NOTES ON PROGRAM OPERATION:",,," - Once a sceen has been plotted, you are given the prompt: ""request or continue:""."," Your choices are:"," f - to snapshot the screen into f$",," p - to print the screen (uses the COPY command)",,," s - to save the screen as an individual SCREEN$ file",,," NOTE: Snapshot is done when the above prompt is displayed again":GO SUB 1500 8075 GO SUB 8500 8080 PRINT " If you wish to increase the size of the movie file, simply change the DIM and LET statement values for f$ and mp in line 1005 to the new size",,,," I hope you enjoy the program. If you have any questions or comments, I'd be glad to hear them!",,,,,"Michael Leidel",,"415 Greenwood",,"Muscatine, IA",," 52761":GO SUB 1500 8085 GO TO rst 8500 CLS :PRINT " WELCOME TO FRACTAL THEATRE..."; AT 2,9;" ** read me ** ",,, 8505 RETURN 9001 REM utilities menu 9005 CLS :PRINT " WELCOME TO FRACTAL THEATRE..."'''' TAB 6;" ** utility menu ** "'''' TAB 6;"1 - CHANGE COLORS"'' TAB 6;"2 - SAVE PROGRAM"'' TAB 6;"3 - QUIT PROGRAM"'' TAB 6;"4 - TOGGLE PAUSE FLAG"'' TAB 6;"5 - RETURN TO MAIN MENU" 9010 INPUT (k$);"select an item: ";i 9015 GO TO (9100 AND i=1)+(9200 AND i=2)+(9300 AND i=3)+(9400 AND i=4)+(0010 AND i=5)+(9010 AND (i<1 OR i>5)) 9101 REM change colors 9105 PRINT AT 8,10;"CHANGE COLORS" 9110 INPUT (k$);"enter BORDER color: ";mbord 9115 INPUT (k$);"enter PAPER color: ";MPap 9120 INPUT (k$);"enter INK color: ";mink 9125 GO TO 9000 9201 REM SAVE program 9205 PRINT AT 10,10;"SAVE PROGRAM" 9210 INPUT (k$);"enter file name: ";n$ 9215 SAVE n$ LINE 1 9220 INPUT (k$);"verify file? ";i$ 9225 IF i$ <>"y" THEN GO TO 9275 9230 PRINT AT 18,4;" ** verifying program ** " 9235 ON ERR GO TO 9260 9240 IF n$(1)="@" THEN VERIFY n$(1)+n$(4 TO ) 9245 IF n$(1) <>"@" THEN VERIFY n$ 9250 PRINT AT 18,4;" ** program is verified ** " 9255 GO TO 9265 9260 PRINT AT 18,0;" ** program is not verified ** " 9265 PAUSE 5 9270 ON ERR RESET 9275 INPUT (k$);"SAVE program again? ";i$ 9280 IF i$="y" THEN GO TO 9200 9285 GO TO 9000 9301 REM end of program 9305 INK 9:STOP 9401 REM togglePAUSE flag 9405 IF pflag THEN PRINT AT 18,8;"pause flag reset" 9410 IF NOT pflag THEN PRINT AT 18,9;"pause flag set" 9415 LET pflag= NOT pflag 9420 GO SUB 1500 9425 GO TO 9000 9998 SAVE "fracthe" LINE 1 ```