Investigating MacPaint's Source Code
MacPaint is a monochromatic raster image painting program that introduced many people to mouse-driven controls, tool palettes, and copy and paste integration with other applications. One of two launch applications for the Apple Macintosh in 1984, MacPaint is emblematic of the Macintosh’s early quirky revolutionary branding, focus on ease of use, and appeal to artistic customers. Using the source code, we examine the design and implementation of the application. We find that the buffer management and bucket filling algorithms demonstrate mechanical empathy with the 68k platform and leverage the limitations of the domain as a means to improve performance. We also find positive and negative aspects in the code style and architecture and its pliability for change. Finally, we dispute some claimed novel aspects of the program while also arguing for its significance and impact on the development of digital graphic systems.

MacPaint 1.5 (1985)
Table of Contents
- Background
- Timeline
- Developer: Bill Atkinson
- Development and Testing
- Design and Source Code
- Interesting Algorithms and Designs
- Alternative Paths and Competitors
- Post Release
- Conclusion
- Special Recognition
- References
Background
At the Boston Computer Society’s general meeting on January 30th 1984, Steve Jobs laid out his rationale for why Apple’s newest product, the Macintosh computer, was the third milestone product of the computer industry after Apple’s own Apple II and the IBM PC. The Macintosh would be the “computer for the rest of us.” The Macintosh used the same software as the Lisa bringing the same ease of use from a point-and-click interface and pull down menus as well as sharing the same fast Motorola 68000 processor.
After highlighting hardware features such as portability, the 3.5" diskette drive, and the AppleBus support, Jobs let the computer do a demo on its own. Booting the Mac from a diskette, the Mac displayed its name and the “Insanely Great” logo, and then showed the first real, application image: a MacPaint screenshot showing a woodcut of a Japanese lady.

MacPaint screenshot with Japanese Lady (1984)
Apple used the screenshot heavily within their advertisements and most of the audience would have seen static screenshots of MacPaint and MacWrite in the months beforehand, but the audience was still delighted.
Twenty-nine minutes in, Jobs introduces a panel of the Macintosh development team and Bill Atkinson starts the first demo, a demo of MacPaint that he developed.
Over the next seven minutes, Atkinson demonstrates how to make art with the program. The audience’s first applause comes when he uses an eraser to erase little chunks of previously drawn lines and rectangles. The applause is due to both how easy and quick the tool is to use but also that it demonstrates the program is working with actual pixels; he isn’t creating new clipping regions or restricted to deleting entire shapes. The audience also applauded when Bill draw lines filled with patterns and then again when, by holding down the spray can, the paint grows denser. Heralding the feature’s value, the fourth applause comes when Bill zooms in and manipulates individual pixels using the Fat Bits mode. The audience similarly loves the many ways areas of the image can be selected, moved and copied.
Atkinson finishes the demo by copying an image of a fish that will soon be pasted into a MacWrite document. In an unspoken nod to the limits of the Macintosh’s hardware, Atkinson closes MacPaint so the memory can be used by MacWrite.
Randy Wigginton, who is about to demo MacWrite, pauses to praise Bill’s work on QuickDraw, the foundational graphical library for the Lisa and the Macintosh. “Without Bill, none of us would be up here on stage.”
Bill Atkinson’s contributions at Apple, including foundational user interface contributions for the Lisa and Macintosh computers, and one of the two application programs that shipped with the “Insanely Great” Macintosh as the advertisements claimed, have been well-recorded. With the release of the QuickDraw and MacPaint source code in 2010, we have an opportunity to examine the technical design and implementation of his work. This article examines the MacPaint application, how it was built, what are some of the interesting algorithms and engineering trade offs, and how we might measure its impact against the larger industry trends around image painting and rastering technology.
Timeline
The development of MacPaint is intertwined with the development of the mouse and the graphical user interface, the Lisa and Macintosh computers, and QuickDraw, the foundational graphics library used by both the Lisa and the Macintosh. This timeline focuses on MacPaint and contemporary competitor painting programs, not on the overall history of raster drawing programs. See (Smith 2001) for a history of early raster drawing programs and their commercial applications and development.
1982
Over a period of six weeks, Atkinson develops a prototype painting program that “sort-of worked” (Young 1985, pg 315). The source code file MyTools.text
, later renamed to MyTools.a
, states it was created October 31st.
1983
In January, Apple announces the Lisa Computer, although no units are shipped until June. The Lisa includes Atkinson’s QuickDraw library.
Microsoft broadens the development of mouse-based applications by releasing their first Microsoft Mouse. The package includes a color raster drawing program program called ‘Doodle.’ Doug Wolfgram releases perhaps the first third-party drawing program with ‘Mouse Draw,’ which uses the Microsoft Mouse.
Atkinson resumes work on MacPaint, at this point called MacSketch. The set of palettes and tools is already very close to the eventual MacPaint UI. The image in the screenshot (below) is celebrating ROM 2.0; based on MyTools.a, this would date the image between February 13th and March 16th, when the file was regenerated for ROM 2.0 but before ROM 2.4.

MacSketch (MacPaint c. 1983; Source folklore.org)
MacSketch is renamed MacPaint in April. Between then and October, Atkinson iterates on the program adding features and improving performance. The last entry in MyTools.a is dated September 1983.
In December, Apple advertises the Macintosh with a full-color brochure. MacPaint is featured prominently and is used to educate the public on how tool palettes, menus, and copy-paste work. The ad also mentions that the content area can be scrolled for more work space.
1984
With great fanfare, the Apple Macintosh is shown at the Boston Computer Society January 30th General Meeting. Bill Atkinson demos MacPaint (and implicitly, QuickDraw) to the crowd. The original Macintosh 128k comes with two applications: MacWrite and MacPaint.
In May, MacPaint 1.3 is released as part of a free software update to customers. This version adds the ability to lasso an object and repeatedly fill it (via the Fill item in the Edit menu) with a pattern.
In September, MacPaint 1.4 is released along with the Macintosh 512k.
Competitors quickly adopt the MacPaint interface. In June, Mouse Systems ships PC Paint 1.0 bundled with a mouse in competition with Microsoft. PC Paint is based on Mouse Draw, which they purchased from Wolfgram, but with a MacPaint-like interface. Similarly, ZSoft Corporation ships their PC Paintbrush, also DOS-based but with an interface derived from MacPaint.
1985
Microsoft releases a new version of their mouse. They drop their ‘Doodle’ program and replace it with a rebranded version of PC Paintbrush, licensed from ZSoft Corporation. In April, Apple releases System Software 2.0 which includes MacPaint 1.5 (https://archive.org/details/mac_Paint_2). This is the last version until 1988, when MacPaint 2.0 is released by a new developer. There are no more official releases of MacPaint.
Developer: Bill Atkinson
When you start MacPaint, the name Bill Atkinson briefly flashes in the credits. You can also find Atkinson’s name and a small portrait in the About menu.

MacPaint 1.5 About Window (1985)
Ironically for someone who would rise to be an Apple Fellow, Bill Atkinson did not receive classical training in computing. His undergraduate education was in chemistry and biochemistry with graduate training in neuroscience. However, he was not disconnected from the computing scene. He built both an IMSAI and an Altair computer (Atkinson 2004, pg 4). Further, his college work emphasized using computers and he made contacts that would prove highly influential to his career.
While at UC San Diego, Atkinson met Jef Raskin and was introduced to Raskin’s unconventional computer lab which emphasized direct and real-time connections with computers. He also met Guy Bud Tribble at UCSD, who would later live with Atkinson and help develop the Macintosh. Atkinson’s mentor at the University of Washington, Kent Wilson, introduced him to computer graphics and innovations in the field, such as Ivan Sutherland’s work on Sketchpad.
Lured to Apple in 1978 by Jef Raskin, Atkinson became the company’s first application software developer. His first project was a stock portfolio evaluator because, while Apple featured one in an advertisement, they did not have any in the catalog (Atkinson 2004, pg 7). His second major project was helping port the UCSD Pascal system to the Apple II. With no other structured programming options, Lisa development adopted this version of Pascal (Atkinson 2004, pg 9).
In 1979, Steve Jobs made visits to Xerox Parc along with a small group of Apple employees, including Atkinson. At Parc they were shown the Alto computer, Smalltalk programming language, and (likely) the Bravo text editor (Atkinson 2004, pg 18). Transferred to the Lisa project, Atkinson was responsible for LisaGraf, the foundational graphics library (later named QuickDraw), as well as the original window manager, menu manager, and event manager (Atkinson 2004, pg 20).
Since the Macintosh re-used software from the Lisa, in particular Atkinson’s user interface and graphics code, he moved onto the Macintosh team where he started to work on MacPaint in earnest in 1983.
Unlike the founders of Adobe, Atkinson did not come from the computer graphics research world, but was familiar with research advances through his academic mentors and had met luminaries such as Douglas Engelbart. Similar to the Adobe founders who worked on the same problems multiple times, he had time to iterate, with several years of experiments as Lisa developed from a research effort into a product. Intentionally straddling both foundational and application development, Atkinson had the viewpoint of a “vertical integrator,” able to control where functionality should go and how the interfaces should work.
Development and Testing
Finding life in the Apple office too “busy” (Atkinson 2004, pg 20), he worked out of a home laboratory using a prototype Apple Lisa. The Lisa had a “Workshop” mode which featured a graphical editor and a command-line environment for compiling and other development activities.
Atkinson took Polaroids of the user interface as it evolved and drove into work to share them with the team. Fortunately for posterity, Atkinson saved the Polaroids and we have a detailed visual history of the evolution of the Lisa interface and QuickDraw capabilities as shown in the 2022 CHM interview below. (SketchPad is briefly shown and discussed, starting at 9:45.)
The Macintosh team used the ‘Monkey’ as a durability and robustness test mechanism. Developed by Steve Capps, the Monkey would randomly type keys, move objects, and interact with menus (Atkinson 2010, pg 14-15). The team used a computer running in Monkey mode to effectively stress-test an application. MacPaint was able to survive two weeks without crashing. Monkey mode can be seen in the source code:
|
|
To prevent Monkey mode from quitting the program and thus ending the test prematurely, the Pascal code calls this function to selectively disable the Apple menu, File menu, and the Quit Program command.
Susan Kare, who served as the graphics designer for the Macintosh, was the main customer of MacPaint. Atkinson watched her use MacPaint and “see what she stumbled on or wished she had” (Atkinson 2004, pg 47). As the only true artist on the team, and someone who used MacPaint as a tool for their job, Kare’s feedback was invaluable. Andy Hertzfeld characterized her impact as “I think a lot of the refinement of MacPaint came from watching an actual user, an actual artist use the program on a day-to-day basis.” (Atkinson 2010, pg 9). Similarly, Atkinson states “I would credit Susan Kare as a co-designer of MacPaint because she used it as I was trying to write it.” (ibid).
Design and Source Code
Physical Description
The MacPaint 1.3 distribution consists of five files:
MacPaint.p
, 4,688 lines of Pascal (Lisa Pascal variant)MacPaint.rsrc
, resource description for the program containing icons, strings, and other localizable attributes. The version string identifies itself as version 1.3.MyHeapAsm.a
, 67 lines of assembly for calls into system memory management routinesMyTools.a
, 300 lines of assembly defining traps or external calls into QuickDraw. A comment states this file was mostly generated viaMakeTTraps
. This is the only file with a change log and content attributed to someone other than Bill Atkinson.PaintAsm.a
, 1,809 lines of assembly containing application code called from the Pascal code
We counted physical lines of code using the pascal_count
and asm_count
programs, both part of David A. Wheeler’s SLOCCount suite.
Data Types and Structures
MacPaint defines very few datatypes for its own use, leveraging instead types from QuickDraw such as Point, Rect (Rectangle), Pattern, and BitMap.
The Pascal code heavily uses global variables; the list extends from lines 212 through 370, with white-space used to group them by commonality. Most of the global variables are used to store various flags or interface state, such as the current font specification. This list is distinct from global constants (lines 27 through 190). Most of the constants are used to specify menu items, buttons, or other interface elements.
The application is a set of tools that, ultimately, modify the document which is a fixed size 1-bit bitmap stored as an array of integers. The document lacks dynamic metadata. As pixels are pretty simple, few abstractions or data types are required.
Pascal vs. Assembly
Roughly one-third of MacPaint’s lines of code are in Motorola 68000 assembly, while two-thirds are in Pascal. In (Young 1985, pg 316), Atkinson explains the rationale and benefit of both languages:
By frequency of working on it, I would bring up the Pascal file 20 or 30 times for every one time I brought up the assembly language file. Basically, the information in assembly language doesn’t really need a lot of maintenance. The assembly-language portion contains things that are there for speed, or that were small and I knew wouldn’t need a lot of maintenance. I put them in assembly language just to reduce the code size. By keeping the main control, flow, and logic in Pascal, the program was more pliable.
Atkinson’s rationale is supported by the list of procedures in each language. Performance-critical code, such as that which manipulates the buffers directly, is in assembly. User interface control logic is in Pascal, as well as code that handles initial setup or rare operations. Operating system calls, such as checking the amount of spare space in the disk drive or invoking the system beep, are in assembly. Since MacPaint was developed concurrently with the operating system, some functionality may have existed in the ROM but had not yet been exposed by Pascal system libraries.
As an example of Atkinson’s assembly style and quality, we present the NearPt
function:
|
|
NearPt
returns true if two points are “close enough,” even if they are not truly equal. Since points use integral values, this code is not for handling floating point error, but for human imprecision. For instance, if a user was trying to close a polygon by clicking on a previous point, the code allows the polygon to be closed if the user clicks within a few pixels of an earlier point. Similarly, the mouse may slip a small distance during a double click. While this is not particularly performance critical code as it is called a relatively small number of times, it is code that is unlikely to need to change.
As is typical for his assembly code, the function is documented with the Pascal calling convention. This function is further documented with a mathematical definition; few functions or procedures warranted descriptive comments. Each line is commented semantically. Almost every assembly line in PaintAsm.a
is similarly commented.
In contrast, HVConstain
is a Pascal procedure. We chose it as an example because it is relatively short and self-enclosed. This procedure is used to constrain or trap an anchor while drawing to a particular direction or a 45 degree angle. For example, the user can hold down shift while drawing a rectangle to force the drawing of a square. Or, while drawing a line, force it to be parallel with the edges of the screen.
PROCEDURE HVConstrain(VAR newPt: Point);
VAR dh,dv: INTEGER;
BEGIN
IF shiftFlag THEN { constrain to horiz or vert }
BEGIN
IF hConstrain AND vConstrain THEN { still chosing direction }
BEGIN
dh := ABS(newPt.h-ptConstrain.h);
dv := ABS(newPt.v-ptConstrain.v);
IF (dh > dv) AND (dh > 1) THEN vConstrain := FALSE;
IF (dv > dh) AND (dv > 1) THEN hConstrain := FALSE;
END;
IF hConstrain THEN newPt.v := ptConstrain.v; { horiz }
IF vConstrain THEN newPt.h := ptConstrain.h; { vert }
END;
END;
Atkinson has a very consistent naming style for his variables and the code is usually highly readable. In contrast to his assembly code, comments are rare and terse in the Pascal code, although still focused on explaining the semantic purpose of the line.
HVConstrain
uses one global variable, shiftFlag
, to track the user mode, and three global variables to store state: hConstrain
, vConstrain
, and ptConstrain
. Although global variables, there is only one other procedure that accesses the latter three variables directly: InitConstrain
. The three variables are commented as belonging to HVConstrain
. The design allows a developer to use the variables without proper initialization or accidentally modifying them. Although this is a violation of the design principle of encapsulation, we believe this was an effective trade-off of code complexity and memory resources.
Message Loop
MacPaint is an early event-driven program. The core of the program is:
REPEAT
[...]
IF GetNextEvent(everyEvent,theEvent) THEN ProcessTheEvent;
[...]
UNTIL quitFlag;
GetNextEvent
is a Toolbox Event Manager function that fetches the next event, if it exists, from the event queue. ProcessTheEvent
is an application procedure that is a long CASE statement that maps event locations to buttons or, more accurately rectangular areas of the screen. ProcessTheEvent
calls other application procedures that control tool-specific modes. ProcessTheEvent
is sufficiently low-level it has to measure the time since the last click to differentiate between single and double clicks. Since the user interface is fixed (windows cannot be moved), the code is tedious but easy to follow.
Example: Straight Line Tool
The Straight Line tool is an exemplar of the software design and the intermix of Pascal, assembly, and QuickDraw functionality. For the StraightLine
procedure to be invoked, the user will have previously selected the straight line tool from the tool palette and then clicked (and held) the mouse within the content area. While in this procedure (the straight line “mode”), one end of the line will be anchored at the initial point (startPt
) while the other end will follow the cursor until the user releases the mouse button. The mode-driven interface gives the user continuous feedback on what the eventual line will look like within the painting.
{$S }
PROCEDURE StraightLine;
VAR newPt,oldPt,startPt: Point;
lineTop,lineBot: INTEGER;
BEGIN
JamLine;
PinGridMouse(startPt);
oldPt.h := 1000; { force first time }
REPEAT
PinGridMouse(newPt);
IF shiftFlag THEN Constrain(startPt,newPt,TRUE);
IF NOT EqualPt(newPt,oldPt) THEN
BEGIN
MainToAlt; { erase old }
AltBufLine(startPt,newPt,oldPt);
oldPt := newPt;
END;
UNTIL NOT StillDown;
END;
The first line {$S }
is a compiler directive stating that this procedure should live in the default main segment (for more about segments, see the section below Allocation Failure and Segment Anti-Fragmentation)
JamLine
resets some QuickDraw state using PenNormal
and then sets the pen’s size, pattern, and mode (depending on keys being depressed) to the current palette settings.
PinGridMouse
sets the passed in variable to the current position of the mouse as modified by various modes (e.g. snap to grid, fat bits). Similarly, Constrain
sets newPt
to a 45-degree constrained point value if the user is holding down the shift key.
The REPEAT
block tests the StillDown
condition. StillDown
is a Toolbox Event Manager function that will return false even if the user released and then quickly pressed the mouse button again between invocations.
If a line exists (IF NOT EqualPt
), the main buffer will be copied into the alt(ernate) buffer. (QuickDraw uses integral values for a Point
’s coordinates, so EqualPt
does not need a tolerance parameter.) MainToAlt
calls BufToBuf
, which is an assembly routine that includes the same MOVEM
optimization as BufToScrn
(see section Fast Buffer to Screen Copy). AltBufLine
writes, to the alt buffer, the line from startPt
to newPt
using the MoveTo
and LineTo
QuickDraw routines. The contents from the alt buffer are then sent to the screen buffer using a boundary box that includes the oldPt
, thus eliminating (by redrawing) any previous line sent to the screen, but also reducing the amount of written data. (BandToScrn
also takes care to hide and show the cursor.)
The lineTop
and lineBot
variables are declared but unused. Our assumption is that part of the functionality of AltBufLine
originally was part of the StraightLine
as it is the only other block to use variables with the same names. The program also uses the constant lineTop
to describe the top of the line size palette window, so the compiler must not have considered duplicate lineTop
declarations as an error.
Interesting Algorithms and Designs
Fast Buffer to Screen Copy
In an interview with the Macintosh team (Lemmon 1984), the team explains their process for optimizing code size and processing time. After arguing that code must be first made correct before being made fast, Atkinson brings up register allocation. He states:
This little baby, the 68000, has sixteen 32-bit registers sitting there, and the way you get performance out of that is to keep them full. Keep the registers full of important stuff all the time. That’s the way you make this processor sing.
The buffer copying code, which is performance critical, illustrates this technique.
MacPaint uses two off-screen buffers for rendering which are then copied to the screen buffer for display. Within the pascal code, the two buffer’s storage are declared as:
mainBuf: ARRAY[0..239,0..12] OF LongInt;
altBuf: ARRAY[0..239,0..12] OF LongInt;
In Pascal, range definitions are inclusive, so each buffer contains 240 rows of 13 LongInts each. The content area is fixed at 416 pixels by 240 pixels. Since a LongInt contains 32 bits, each row stores 416 single-bit pixels.
While MacPaint’s user interface looks well-proportioned to the Macintosh’s display, Atkinson could have designed the content area to be larger or adjusted the design for a portrait arrangement. However, the need to keep registers full suggests the technical reason for the given layout. If we look at the BufToScrn
assembly code:
|
|
The NXTROW
loop is executed 240 times with each execution copying 13 LongInts from the buffer. The Motorola 68k supports a “move multiple” (MOVEM) instruction that accepts up to 13 registers as the source or destination. (The .L
informs the assembler we are copying long values.) According to (Motorola 1993), a MOVEM instruction requires \(12 + 4n\) clock periods to move memory from an address stored in an A register to registers and \(8 + 8n\) clock periods to move values stored in registers to a memory location (Table 9-16). Thus, memory transfers require \(20 + 12n\) (where \(n\) is the number of registers) or 176 clock periods. In contrast, if the transfers were performed via MOVE instructions, each transfer requires 12 clock periods or 312 clock periods total (Table 9-18). By maximizing the throughput possible with MOVEM by populating all (relevant) registers, each buffer to screen transfer saves 136 clock periods.
Bucket Fill (Seed Fill)
Bucket Fill is an algorithm that, starting from a given pixel, travels along all adjacent pixels sharing a base color and transforms those pixels to a target color. MacPaint implements a variant of this function by filling the space with a pattern, rather than just a single color. As the boundaries can be arbitrarily complex, this can be an expensive computation. In the worst-case, filling an empty screen, this requires 416 by 240 pixel checks or 99,840 checks in total. Through various tricks, Atkinson’s implementation reduces the amount of work and makes the operation feel fast.
(This algorithm is not used when the boundaries are known, such as painting a filled in rectangle. QuickDraw supported drawing filled in polygons directly.)
By 1983, several researchers had investigated and published algorithms for bucket filling. SuperPaint, in the mid 1970s, was likely the first implementation of the idea (Glassner 2001); (Lieberman 1978) and (Smith 1979) represent early research papers. The MacPaint algorithm is similar to Lieberman’s algorithm as the algorithm travels along vertical and horizontal pixel paths and supports filling in with patterns as well as colors. MacPaint’s code has been optimized for the restrictions in the domain, i.e. one-bit bitmaps.
Within the source code, the Bucket Fill tool is called SeedFill
. The SeedFill
procedure contains the top-level business logic for the tool:
|
|
Based on the position of the mouse click (startPt
) and the current state of the image (mainBits
), the procedure determines if the clicked pixel is black (firstBlack
is true) or white. The assembly CalcMask
procedure travels the image in mainBits
, from the startPt
, and computes a mask for the eventual pattern, storing this in altBits
.
After the boundaries for the fill are determined by CalcMask
, the graphics port is set to the alternate buffer and the pattern is set to the currently selected pattern. The pattern transfer mode (patBic
or notPatBic
) is set to effectively erase the destinations content. The pattern is then filled within the bounding box, inverting the color depending on if the object’s boundary is a black or white pixel. Afterwards, the alternate buffer is sent to the screen and the procedures performs some cleanup. If the result is not what the user wishes, they can undo the action, whereupon the screen switches to the other buffer which contains the content prior to the fill.
CalcMask
contains the complicated parts of the program. CalcMask
is also shared with the Lasso tool, which similarly needs to match arbitrary shapes.
|
|
At a high-level, CalcMask
is very similar to (Lieberman 1978)’s fill algorithm. The initial point, the seed, is expanded left and right until a boundary is found, and then the program checks up and down. Lieberman’s version uses a queue, while Atkinson’s uses two arrays as queues. Because a queue can be completed consumed before ‘ping pong’ing to the other queue, iteration and appending items are simpler than using a single queue.
The MaskIt
procedure scans right and left from an initial position, and then seeks to expand up and down the image. Based on the movement direction, the algorithm is looking for boundary conditions. Two lower-level algorithms implement much of the bit-level logic: HSeed
and VSeed
. Both are “local functions,” they are effectively inlined functions within the assembly rather than functions callable (and requiring a stack frame) from Pascal. We will focus on VSeed
because it is simpler.
Conceptually, VSeed
is trying to extrude the seed up (or down, the directions are equivalent) through the “grill” of the data. If a slice of the seed can flow into an open space of the grill, it will then also flow right and left within the grill to the edge or to a boundary. For clarity of visualization, in the table below we use a 3-bit word size. The one bits in the mask represent the set bits of the target color being “pushed” into the data. The data may be full of the target color, in which case we push into the entire “word”. However, we might encounter a wall, such as “data empty”, in which case we are unable to push to any location, resulting in zero. In the case of a hook, we flow both up and to the open left.
Open | Blocked | Same | Offset | Hook Left | Hook Right | |
---|---|---|---|---|---|---|
Data | 111 | 000 | 010 | 100 | 110 | 011 |
Mask | 010 | 010 | 010 | 010 | 010 | 010 |
Result | 111 | 000 | 010 | 000 | 110 | 011 |
The assembly code implementation:
|
|
The first half of VSeed
is equivalent to the pseudo-code:
switch (mask bitand data)
case 0: return 0
case -1: return -1
case mask /* mask = data */: return mask
The last half performs iterative bit-twiddling to flow the seed into adjoining spaces of data.
The test of the seed against the earlier data also demonstrates the value of ‘playing the odds.’ Although boundaries may be arbitrary, they are often regular in shape – e.g. a boundary being a vertical line. Expensive computations can be reused if a relatively simple check is made first. Atkinson relates this technique to a similar domain in (Lemmons 1984, pg 76):
So play your odds. People draw characters in OR mode a whole lot, and OR mode is about twice as fast as the other modes, so 95 percent of all characters are drawn in OR mode. Statistical measuring of the use of the thing allows you to get much more performance on your average throughput than you can if you don’t go back and measure.”
In cases where the fillable area is open, or edges are regular, the algorithm can effectively process a 32 pixels at a time with much higher throughput than checking each individual pixel.
FatBits
One of the “Goodies” – additional MacPaint features selectable via the menubar – FatBits is a mode that magnifies the image. When in this mode, a small picture-in-picture provides the artist context of the full image while the rest of the content area expands to make it easier to manipulate individual pixels (below). Originally MacPaint only allowed the Pencil tool to be used in FatBits mode, but Atkinson extended support for all the tools (Young 1985, pg 315).

MacPaint 1.5 in FatBits Mode (1985)
As suggested by the title of an early manual Inside MacPaint: Sailing through the Sea of FatBits on a Single-Pixel Raft, FatBits was a killer feature for MacPaint. For Susan Kare, Apple’s in-house graphic designer, the mode accelerated the development of Macintosh’s small bitmap icons where every pixel mattered.
Marketing pushed to rename “FatBits” “Magnify”, but Atkinson won the argument, arguing that the name gave the program some personality (Young 1985).
FatBits is restricted to a single zoom level and, within the code, is handled via many special-cased changes to the input and output. The single zoom level restriction was due to the limited hardware; an arbitrary scaling factor would have required too much CPU (Atkinson 2010, pg 10). The contemporary PostScript program supported arbitrary scaling via a transformation matrix but operated with looser performance requirements and greater hardware capabilities.
While an important and impactful feature, IF fatFlag
is called 36 times in the Pascal code. A cross-cutting feature, it requires special handling when mapping input coordinates, screen rendering, and buffer manipulation. Thus, we consider it an expensive feature because it violates the open-closed design principle — existing code needed to be modified to support it and future tools need special-cased code to support the mode. Functional composition is used to reduce the impact in certain code paths (e.g. GetFatMouse
calls GetMouse
and internally performs any necessary conversion), but there are too few abstractions overall to keep the cross-cutting nature of the feature constrained.
Allocation Failure and Segment Anti-Fragmentation
As a way to work within the limited memory of the Macintosh, developers could divide their application code into multiple segments. A segment could be unloaded when not needed, for example, printing routines (Inside-Vol2 1985, pg II-55). In the Pascal code, the compiler directive {$S SegPrint }
places the procedure or function into the SegPrint
segment. An empty name (e.g, {$S }
denotes the default, main segment.) In the assembly code, .SEG
is used.
Other than the main segment, MacPaint has the following eleven segments: SegBrush, SegFlip, SegHelp, SegInit, SegPage, SetPaste, SegPatEdit, SegPrint, SegScrap, SegSym, and SegUpdate.
Within the main event loop, each of these segments is unloaded after each loop. Per (Inside-Vol2 1985, II-57), this is recommended practice as unloading a segment that is not loaded is a no-op and presumably cheap.
Although a program may have sufficient free space to work, the layout of allocated memory might not leave a block large enough for an allocation to succeed. MacPaint’s design was robust to a certain level of memory fragmentation.
Within the main event loop, the code tests for heapJam
to be set and, if it is, calls the MaxMem
system routine. This routine compacts and frees unused memory, acting as a kind of garbage collector and compactor. Once fragmentation is reduced, the next memory allocation is likely to succeed.
|
|
MacPaint’s implementation largely avoids dynamic memory allocation, but there are eight calls to NewHandle
(which allocates memory). Of these, only AllocMask
tests the return value of NewHandle
to verify it is non-Nil and sets the heapJam
global variable if it is. The reason why AllocMask
is the only path that checks for a memory allocation failure may be due to its unusually large allocation request. AllocMask
asks for 12,480 bytes to store a mask which is nearly 10% of the Macintosh’s 128k memory in total. Other than some calls that are used for initialization, the next largest called to NewHandle
is 3,024 bytes and the rest below 100 bytes. As these other calls are much less likely to fail, it is perhaps acceptable that they lacked similar error detection.
All callers of AllocMask
check the return status and return early if the call failed. Although the user would need to retry their operation, the program would not suffer a crash due to the failed memory allocation.
Atkinson recounted this general technique in (Atkinson 2010, pg 15):
When code segments were loaded, you needed some code to do this job and that job. They would be loaded sort of at the first available place. But if you needed another code segment and this one would go out, it might leave a hole there that wasn’t quite big enough for the next one that you needed but now you had sort of what we called memory fragmentation. That even though you had enough memory total, you couldn’t load the pieces of code that you needed. And I developed a little technique for this which is setting a flag at the top of the event loop, saying that we failed and as I went to load code segments, if I failed to load one then I would beep and let it go back to the top of the event loop without doing anything. And it would say uh-oh, there’s a failure here. Whereas if I succeeded and got to the right part of the code and got everything in, then I would set the flag to say we succeeded. The net result was the user would go to draw something and it would beep and they would try it again and it would work, and they’d shrug and they’d never know that they just avoided crashing the program.
Alternative Paths and Competitors
As a counter-factual, if MacPaint had not shipped with the Macintosh, Apple may instead have shipped a vector drawing program. The Lisa shipped with LisaDraw, a vector drawing program which was the basis for MacDraw. However, MacDraw was likely not available in time for the January launch, as the September/October issue of MacWorld states MacDraw is “soon-to-be-released”. Since MacPaint was finished several months prior to the Macintosh’s release date, we are unaware of any historical “Plan B”.
Competively, there were several digital painting programs released in parallel. The year prior to the release of MacPaint, 1983, saw the release of the first Microsoft Mouse and two drawing programs that used it: Microsoft’s Doodle program (below) and Doug Wolfgram’s Mouse Draw. Notably, both programs supported color but the user interfaces can be charitably treated as primitive.

Microsoft Doodle (1983)
After the release of MacPaint and the Macintosh at the beginning of 1984, competitors quickly adopted the user interface. Mouse Systems purchased the rights to Mouse Draw, modified the interface to be similar to MacPaint, and then resold it as PC Paint 1.0 along with their own mice. ZSoft Corporation released their own PC Paintbrush, also with an interface modeled after MacPaint. Both programs were DOS-based and supported color. A year later, Microsoft updated their mouse and dropped Doodle, replacing it with a licensed but rebranded version of PC Paintbrush.
Thus, the demand for painting programs, tied to sales of mice, was recognized independently of the Macintosh and multiple vendors were attempting to fill that need. That said, within the consumer market, MacPaint seems to be the first “great” painting program and highly influential.
Technologically, was MacPaint novel? The site macpaint.org claims the following aspects of MacPaint were novel:
The marching ants around a selection; the palette of drawing tools; the (rudimentary) ability to zoom in; the spray can; the paint bucket; copying and pasting images between programs; just moving the mouse and drawing: we take these for granted in the 21st century, but Macpaint did them first, with only 128k of RAM available.
While we concede the marching ants and maybe the spray can were novel to MacPaint, MacPaint has historical antecedents in research and commercial systems. As a contrary example, SuperPaint, which displayed its first picture in 1973, supported color, and featured a tool palette and the following functions: Paint, Shrink 2x, Expand 2x, Move, Copy, Store and Load, Text, Video In, Make Brush, Draw Lines, Gridding, Area Fill, and various color table animation tools (Shoup 2001). Originally aimed at television graphics use cases, this research system was rapidly supplanted by commercial offerings. For example, the Quantel Paintbox, a dedicated computer for compositing broadcast video and graphics, was first released in 1981 and was quickly adopted by the major TV networks.
Although the Macintosh was opening up new markets for consumer and small-scale digital production, broadcasting and commercial computer animation firms had been operating for several years. In Alvy Ray Smith’s article “Digital paint systems: an anecdotal and historical overview” (Smith 2001), he focuses on “systems” rather than “programs” and eliminates 1-bit and 3-bit systems from his article’s scope because of their lack of influence. MacPaint is thus eliminated both due to its limited functionality and its 1-bit nature. Is this restriction fair?
Certainly, the professional market was developing on its own, without regard for the hobbyist market. The Macintosh was not capable until the late 80s to support the monied use cases which required color and higher-performance. However, consumer technology eventually caught up and the existence of consumer-level tools provided a path for artists to “graduate” to professional-grade tools. Photoshop is an example of this progression.
The Knoll brothers, who developed Photoshop in 1988, were both exposed to the original Macintosh when their father bought one in mid-1984. Thomas Knoll used a Macintosh Plus in 1987 to assist in his PhD work, which also came with MacPaint. The Plus did not support gray scale, so Thomas started working on image manipulation routines. John, who was working at Industrial Lights and Magic at the time, saw connections between what Thomas was developing and the features in the Pixar Image Computer. Combining the use case insights from professional animators with a graphical user interface led to Photoshop.
Photoshop was widely adopted both by professionals but also by aspiring students and hobbyists. It represents a joining of two pathways, one from the personal, consumer world, and the other from the research and commercial world.
Impact of Source Code?
In 1984, event-driven programming was a relatively new paradigm in software design. A possible additional influence of MacPaint was the program being used to train developers on how to develop an event-driven program and how to program for the mouse. Atkinson believes Apple gave the source to some developers [Atkinson 2010, pg 8], although we have not seen evidence for this elsewhere. (At least among external developers; the Macintosh team certainly had access to the code and there are three programmers attested in MyTools.a, including Andy Hertzfeld.) Inside Macintosh, the official developer documentation, includes an Example Program that demonstrates basic window management and handling. Comparing the Example Program to MacPaint, we find there are similarities in naming and structure, which suggests MacPaint may have been a reference when writing the documentation.
Post Release
MacPaint saw two more releases in 1984, the first as a free update with System Software 1.1 and the second with the release of the Macintosh 512K model. In 1985, System Software was updated to version 2.0 and included version 1.5 of MacPaint.
Under pressure from third-party developers for less competition from system shipped software, in 1987 Apple spun off their internally built applications to the Claris company, including MacPaint. Claris released version 2.0 of MacPaint in 1988 which featured tear off palettes, support for multiple documents open at once, the magic eraser, and other features.
The release seemed also perfunctory, as evidenced by the developer working on the 2.0 release to fill time and the lack of any product specs. By April 1988, as listed by Macworld, MacPaint was in competition with an array of graphics programs, including PixelPaint, Modern Artist, Aldus Freehand, and Adobe Illustrator. Furthermore, the Macintosh II had been announced in 1987 with support for 8-bit color, limiting the appeal of a monochrome only application. No more versions were officially released and Claris discontinued the product in 1998.
Conclusion
(Knuth Shustek, 2021) has this to say about MacPaint and QuickDraw’s source code:
They are brilliant programs, beautifully organized and structured, that are a treat to read and deserve to be annotated and studied.
This article does not cover QuickDraw, but do we agree with these claims as they relate to MacPaint?
Is it brilliant?
It is uncontroversial that MacPaint strongly influenced the user interfaces of painting and drawing programs. Contemporary competitors changed their UIs to be similar to MacPaint and much of interface design live on in Photoshop, Illustrator, and other programs. Many of the tool modifiers, such as the shift key to constrain boxes and lines, became informally standardized. Although MacPaint was not the earliest painting program and did not invent all it is often credited with, it was the first “great” implementation, was widely available, and helped cement Macintosh’s reputation as a computer for creative people.
Is it beautifully organized and structured? A treat to read?
This is subjective, so we’ll examine certain aspects of the code.
If we look at the physical sequencing of functions and procedures within the code, we find that the first five callables within the PaintAsm.a
file are:
- EjectReset (eject disks)
- PixelTrue (return state of specific pixel within bitmap)
- Monkey (test for reliability testing mechanism)
- Stretch2x (re-samples buffer for higher resolution printing)
- MySetItemStyle (type coercion utility)
Within the Pascal MacPaint.p
file, after a long list of EXTERNAL declarations, we find:
- KeyIsDown (function to test if a specific key is pressed)
- GetFatMouse (get mouse position in fat bit coordinates)
- GridPoint (modify point to a grid or truncated position)
- GetGridMouse (get mouse position in grid coordinates)
- PinGridMouse (clamp grid mouse position within rectangle)
We reject the idea that the physical organization of the code is “beautifully organized”; if someone wanted to understand the core functionality and design of the program, they should not read it linearly. However, we suspect this was not the foundation of Knuth and Shustek’s claim, as outside of very short or a few literate programs, programs are not read linearly.
Readers will often start with the entry point to the program. In this case, the reader will find a significant number of lines dedicated to UnloadSeg
and MoreMaster
, both efforts to mitigate the constrained memory. Outside of that, the master block contains sequence of Init...
calls, followed by the main loop. The naming and structure facilitate distinguishing between the business logic and the system interfaces. So, we find that the main block is not necessarily beautiful, since it interleaves both kinds of logic, but the organization and structure are praise-worthy.
Within a procedure or function, Pascal mandates some aspects of the ordering, but we find that the code does not fight language idioms. In general, in terms of code style, we find it is easy to read and follow. Anecdotally, while we were writing this article, another programmer saw the Pascal code on our laptop screen (main method) and noted that it appeared clean. (They also thought it looked “old” and certainly procedural code has a different visual appearance than functional or OOP code.)
Does it deserve to be annotated and studied?
There are three primary audiences for a historical source code artifact: 1) historians, interested in the context and impact of an artifact, 2) software practitioners, who can see it as a way to improve their craft, and 3) students, who may be introduced to techniques, practices, and paradigms and use it as training material.
For historians, MacPaint exhibits the engineering trade-offs necessary to bring forth an application within the limitations of the Macintosh hardware, as well as an example of how a graphical program was expected to be designed by someone who also wrote one of the largest, most foundational libraries. Historians of algorithms can place the use of multiple buffers and the seed fill algorithm into the published history of graphics algorithms and how that informs the interrelation of commercial and academic research.
Unless a practitioner is working within extreme resource limits or is working on legacy devices, we suspect the MacPaint source code has less to offer them. User interfaces are now asynchronous and callback based, languages are far richer than Pascal in their support of abstractions and encapsulation, and few use cases are restricted to 1-bit displays.
However, we see many advantages as an artifact of study for students of computer science. First, MacPaint is not a toy, but a full-featured program, yet is quite small so there is little to distract the student. MacPaint’s domain — painting — is readily understood and does not require students to understand unfamiliar domains such as finance or physics. Students are expected to understand multiple languages. Pascal and M68k assembly test a student’s ability to learn a new language while neither is particularly difficult nor obscure.
Although MacPaint runs in emulation and students can easily play with it, a disadvantage is that students would have difficulty modifying the source code and running their own versions. MacPaint doesn’t even run on late series Macintoshes as it was too tightly bound to the hardware. That said, there are many potential assignments that do not require modifying and running the source code:
- Code Explanation. A student prepares a presentation or document explaining how a certain tool function is implemented or how a data structure is used. More advanced students might calculate the computational complexity of an algorithm.
- Design Alternative. A student shows how a certain feature might be re-designed with features from a different programming language or within a certain framework. Compare and contrast qualitatively and quantitatively.
- Develop Tooling. Pascal is relatively easy to parse and Apple’s documentation on Lisa Pascal features detailed railroad diagrams for the syntax. Students could write tooling, similar to
cflow
, to analyze the program. (This tooling be reused to analyze the Lisa source code.)
So, yes, MacPaint deserves to be studied.
Special Recognition
Releasing the source code was surprisingly long and difficult. I thank those involved for their dogged persistence that gave me the opportunity to study this source code.
References
(Atkinson 2004) Atkinson, Bill, and Andy Hertzfeld. 2004. MacPaint oral history with Bill Atkinson and Andy Hertzfeld Interview by Grady Booch. Oral history collection. https://www.computerhistory.org/collections/catalog/102658007.
(Atkinson 2010) Atkinson, Bill, and Andy Hertzfeld. 2010. MacPaint Interview and Demonstration with Bill Atkinson and Andy Herzfeld. https://www.computerhistory.org/collections/catalog/102743021.
(Glassner 2001) Glassner, Andrew. 2001. “Fill ’er up! [Graphics Filling Algorithms].” IEEE Computer Graphics and Applications 21 (1): 78–85.
(Inside-Vol2 1985) Apple Computer, Inc. 1985. Inside Macintosh. Vol. 2. Addison-Wesley Publishing Company, Inc. https://vintageapple.org/inside_o/pdf/Inside_Macintosh_Volume_I_1985.pdf.
(Knuth Shustek, 2021) Knuth, Donald, and Len Shustek. 2021. “Let’s Not Dumb down the History of Computer Science.” Communications of the ACM 64 (2): 33–35. https://doi.org/10.1145/3442377.
(Lemmons 1984) Lemmons, Phil. 1984. “An Interview: The Macintosh Design Team.” BYTE, February 1984.
(Lieberman 1978) Lieberman, Henry. 1978. How to color in a coloring book. In Proceedings of the 5th annual conference on Computer graphics and interactive techniques (SIGGRAPH ‘78). Association for Computing Machinery, New York, NY, USA, 111–116. https://doi.org/10.1145/800248.807380
(Shoup 2001) Shoup, Richard. 2001. “SuperPaint: An Early Frame Buffer Graphics System.” IEEE Annals of the History of Computing 23 (2): 32–37.
(Smith 1979) Smith, Alvy Ray. 1979. Tint fill. SIGGRAPH Comput. Graph. 13, 2 (August 1979), 276–283. https://doi.org/10.1145/965103.807456
(Smith 2001) Smith, Alvy Ray. 2001. “Digital Paint Systems: An Anecdotal and Historical Overview.” IEEE Annals of the History of Computing 23 (2): 4–30.
(Young 1985) Young, Jeffrey S. 1985. Inside MacPaint: Sailing through the Sea of FatBits on a Single-Pixel Raft. Microsoft Press.