Shoebot¶
Shoebot is a tool to automate the process of drawing vector graphics using a minimal and easy-to-understand syntax.
It is a rewrite of Nodebox 1 by Frederik de Bleser and Tom de Smedt, with the purpose of having an equivalent tool in GNU/Linux systems. Nodebox 1 is itself based on DrawBot by Just van Rossum. Shoebot draws using the cross-platform Cairo graphics engine.
It follows a rich lineage of tools dedicated to generative creativity:
For more about the nature of creative coding and generative design, be sure to read The Nodebox 1 theoretical introduction.
Purpose¶
Shoebot is a useful tool for many use cases:
creating generative and procedural works, either for screen or print output
teaching code to non-developers by means of immediate visual feedback
prototyping visualizations and design concepts
automatically generating sets of vector images
live-coding and real-time tweaking of animated graphics
making Cairo-based tools and experiments using a simpler language
Features¶
Originally built as a GNU/Linux version of Nodebox 1, Shoebot comes with many batteries included:
supports most of Nodebox 1’s functionality
user-friendly code editor
headless mode for quick execution without the GUI parts
run on a window or output files in PDF, PNG, SVG or PS formats
tweak running scripts via a simple GUI, a socket server or a text-based shell
can be loaded as a Python module to work inside existing programs
Links¶
Documentation Index¶
Installation¶
Shoebot runs on Python 3.7 and above.
GNU/Linux¶
Installing dependencies¶
You need a few software packages on your system before installing Shoebot.
You can either run the handy ./install/install_dependencies.sh
script, or
paste the command relevant to your distribution from the list below.
Debian and Ubuntu:
sudo apt install build-essential gir1.2-gtk-3.0 gir1.2-rsvg-2.0 \
gobject-introspection libgirepository1.0-dev libglib2.0-dev \
libgtksourceview-3.0-dev libjpeg-dev libpango1.0-dev python3-dev python3-gi \
python3-gi-cairo python3-wrapt
Arch and Manjaro:
pacman -S cairo gobject-introspection gobject-introspection-runtime gtk3 \
gtksourceview3 libjpeg-turbo librsvg pango python python-cairo python-gobject \
python3-wrapt
Fedora and CentOS:
sudo yum install cairo-gobject redhat-rpm-config gcc cairo-devel \
libjpeg-devel python3-devel python3-gobject python3-wrapt
SuSE:
sudo zypper install gcc libjpeg62-devel python-gobject python-gobject-cairo \
python3-wrapt
Installing Shoebot¶
It is recommended to install Shoebot locally, although it can be also be
installed system-wide with sudo
.
python3 setup.py install
If you prefer using virtual environments, we recommend using virtualenvwrapper. These are the steps:
mkvirtualenv shoebot -p $(which python3)
python3 setup.py install
To run Shoebot in the future, you will need to activate the environment first
with workon shoebot
.
Mac OS X¶
Installation on Mac OS X is identical to GNU/Linux based distributions.
Dependencies can be installed with the install_dependencies.sh script mentioned above, or by running:
pip3 install wrapt --user
brew install cairo gobject-introspection gtk+3 gtksourceview3 jpeg libffi \
librsvg py3cairo pygobject3
Windows¶
Shoebot will install and run on Windows 64-bit (7 and above) using msys2.
The necessary dependencies can be installed by downloading and running the
install_dependencies.sh
script. Save this file to your Msys2 home\user
directory (the default is
C:\msys64\home\%YourUserName%\
), run Msys2 Sys
from the Start Menu and
enter:
./install_dependencies.sh
When that is complete, enter:
git clone https://github.com/shoebot/shoebot
When that is done, run MinGW 64-bit
from the Start Menu and enter:
cd shoebot
python setup.py install
After installing, the compiled executables can be used without running the Msys2 shell.
Trouble?¶
Installation is the trickiest step in Shoebot, and can be more challenging than we’d like. If you run into install problems, check the troubleshooting page.
Getting Started¶
For your first time with Shoebot, you can try out the included code editor by
running shoebot
in your terminal.

Open one of the example scripts and Run it through the Run -> Run script menu
option or the Ctrl-R
keyboard shortcut. A window should open with an image
or animation.
You can now start editing the code and re-running to see the new outcome. Changing values on example scripts is a great way to understand how things work.
The Shoebot code editor is however rather limited. We recommend trying out Atom with the Shoebot extension for a more powerful and customizable coding environment.
Running in the console¶
If you prefer using your own text editor to edit files and just want something
to run the scripts, the sbot command is what you want. Head over to the
examples/
directory and try running:
sbot grid/balls.bot
By default, Shoebot will open a window with the result. But we can also
output directly to a file with the -o
option, short for --outputfile
.
Supported formats are SVG, PNG, PDF and PS.
sbot grid/balls.bot -o balls.svg
There are many features available in the console runner that aren’t accessible in the Shoebot or Atom editors, so be sure to take a look at the Command line flags section.
Exporting video¶
The Atom extension has a right-click option to save to
video, but you can also do this in the command line with the
sbot-export-video
script, which generates an image sequence and runs
ffmpeg
to put everything into an MP4 file.
sbot-video-export animation/wishyworm.bot -o worm.mp4 -f 150
The -f
option (short for --framenumber
) specifies the number of frames
to render; the default value is 300, or 10 seconds in 30 FPS.
In the future, this feature will become part of the sbot
command line
runner.
Tutorial¶
The original Nodebox tutorial pages are an excellent introduction to the concepts that you’ll also find in Shoebot.
Introduction – generative art, computational design, and getting started
Basics
Environment – the basics about the Nodebox working environment
Primitives – command parameters and drawing shapes
Graphics state – how shapes can be rotated and transformed
Data
Strategy
Repetition – doing things over and over
Commands – creating custom behaviors
Classes – more advanced coding concepts
Libraries – going beyond the core
Specifics
Interaction – working with the keyboard and mouse
Color – grasping color objects
Math – geometry and other applied math concepts
Bézier Paths
Paths – the basics of Béziers
Manipulating paths – tweaking points on a path
Path Mathematics finding points in a path, inserting new ones
Path Filters applying effects on a path
Compound paths – path operations (unsupported?)
Clamping paths – limiting paths to a box (unsupported?)
Advanced
We’ve omitted the tutorials that relate to Mac-specific details or non-implemented features (like Psyco support). See the Nodebox tutorial page for all of them.
Reference and examples¶
This is the full list of commands available in Shoebot. For a more technical outline of the Shoebot API, see the API reference.
Many parts of this reference were adapted from the original Nodebox documentation by Frederik de Bleser and Tom de Smedt.
Note that most examples here are drawn on a 100x100 size for simplicity; the Shoebot default is 300x300px.
Shapes¶
- rect(x, y, width, height, roundness=0, draw=True)¶
Draw a rectangle.
fill(0.95, 0.75, 0) rect(10, 10, 35, 35) # see how roundness affects the shape rect(55, 10, 35, 35, 0.3) rect(10, 55, 35, 35, 0.7) rect(55, 55, 35, 35, 1)
- rectmode(mode=None)¶
Change the way rectangles are specified. Each mode alters the parameters necessary to draw a rectangle using the
rect()
function.There are 3 different modes available, each expecting different parameters:
CORNER mode (default) – origin point and dimensions (width and height)
x-value of the top left corner
y-value of the top left corner
width
height
CENTER mode – draw a shape centered on a point
x-coordinate of the rectangle’s center point
y-coordinate of the rectangle’s center point
width
height
CORNERS mode – origin point and destination point
x-coordinate of the top left corner
y-coordinate of the top left corner
x-coordinate of the bottom right corner
y-coordinate of the bottom right corner
So while you always specify 4 parameters to the
rect()
function, you can userectmode()
to change the function’s behaviour according to what might suit your script’s needs.nofill() strokewidth(2) rectmode(CORNER) # default, red stroke(0.8, 0.1, 0.1) rect(25, 25, 40, 40) rectmode(CENTER) # green stroke(0.1, 0.8, 0.1) rect(25, 25, 40, 40) rectmode(CORNERS) # blue stroke(0.1, 0.1, 0.8) rect(25, 25, 40, 40)
- ellipse(x, y, width, height, draw=True)¶
Draw an ellipse by specifying the coordinates of its top left origin point, along with its width and height dimensions. See
ellipsemode()
for other ways of drawing ellipses.ellipse(10, 20, 30, 60) ellipse(50, 30, 40, 40) # circle
This command is identical to the
oval()
command from Nodebox, which is also supported and works the same.
- ellipsemode(mode=None)¶
Change the way ellipses are specified. Each mode alters the parameters necessary to draw an ellipse using the
ellipse()
function.It works exactly the same as the
rectmode()
command.nofill() strokewidth(2) ellipsemode(CORNER) # default, red stroke(0.8, 0.1, 0.1) ellipse(25, 25, 40, 40) ellipsemode(CENTER) # green stroke(0.1, 0.8, 0.1) ellipse(25, 25, 40, 40) ellipsemode(CORNERS) # blue stroke(0.1, 0.1, 0.8) ellipse(25, 25, 40, 40)
- line(x1, y1, x2, y2, draw=True)¶
Draw a line from (x1,y1) to (x2,y2).
stroke(0.5) strokewidth(5) line(20, 20, 80, 80) line(20, 80, 80, 20) line(50, 20, 50, 80)
- arc(x, y, radius, angle1, angle2, type=CHORD, draw=True)¶
Draws a circular arc with center at (x,y) between two angles.
The default arc type (CHORD) only draws the contour of the circle arc section. The PIE arc type will close the path connecting the arc points to its center, as a pie-chart-like shape.
nofill() stroke(.2) autoclosepath(False) arc(50, 50, 40, 0, 180) arc(50, 50, 30, -90, 0) stroke('#ff6633') arc(50, 50, 20, 0, 270, type=PIE)
- arrow(x, y, width, type=NORMAL, draw=True)¶
Draw an arrow with its tip at (x,y) and the specified width. Its type can be NORMAL (default) or FORTYFIVE.
arrow(50, 40, 40) arrow(90, 40, 40, FORTYFIVE)
- star(x, y, points=20, outer=100, inner=50, draw=True)¶
Draw a star-like polygon with its center at (x,y).
Following the coordinates, this command expects the number of points, the outer radius of the star shape, and finally the inner radius.
star(25, 25, 5, 20, 10) # top left star(75, 25, 10, 20, 3) # top right star(25, 75, 20, 20, 17) # bottom left star(75, 75, 40, 20, 19) # bottom right
Paths¶
- beginpath(x=None, y=None)¶
Start a new Bézier path. This command is needed before any other path drawing commands such as
moveto()
,lineto()
, orcurveto()
. Finally, theendpath()
command draws the path on the screen.If x and y are not specified, this command should be followed by a
moveto()
call.
- moveto(x, y)¶
Move the Bézier “pen” to the specified point without drawing.
Can only be called between
beginpath()
andendpath()
.
- lineto(x, y)¶
Draw a line from the pen’s current point to the specified (x,y) coordinates.
Can only be called between
beginpath()
andendpath()
.
- curveto(x1, y1, x2, y2, x3, y3)¶
Draws a curve between the current point in the path and a new destination point.
The last two parameters are the coordinates of the destination point. The first 4 parameters are the coordinates of the two control points, which define the edge and slant of the curve.
Can only be called between
beginpath()
andendpath()
.x, y = 10, 62 # Start curve point x1, y1 = 50, 115 # Left control point x2, y2 = 75, 10 # Right control point x3, y3 = 115, 62 # End curve point # Only strokes autoclosepath(False) nofill() strokewidth(12) stroke(0.1) # Draw the curve beginpath() moveto(x, y) curveto(x1, y1, x2, y2, x3, y3) endpath() # To show where the control points are, # we draw helper lines strokewidth(2) stroke(1, 0.2, 0.2, 0.6) # The first control point starts at the # x, y position line(x, y, x1, y1) # And the second control point is the # end curve point line(x2, y2, x3, y3)
- arcto(x, y, radius, angle1, angle2)¶
Continues the path with a circular arc in a way identical to
arc()
. A line will be drawn between the current point and the arc’s starting point.
- closepath()¶
Close the path; in case the current point is not the path’s starting point, a line will be drawn between them.
- endpath(draw=True)¶
The endpath() command is the companion command to beginpath(). When endpath() is called, the path defined between beginpath() and endpath() is drawn. Optionally, when endpath(draw=False) is called, the path is not drawn but can be assigned to a variable and drawn to the screen at a later time with the drawpath() command.
- drawpath(path)¶
Draws a path on the screen. A path is a series of lines and curves defined between beginpath() and endpath(). Normally, endpath() draws the path to the screen, unless when calling endpath(draw=False). The path can then be assigned to a variable, and this variable used as a parameter for drawpath().
Note: if you have one path that you want to draw multiple times with drawpath(), for example each with its own rotation and position, you need to supply a copy with
drawpath(path.copy())
.stroke(0.2) beginpath(10, 10) lineto(40, 10) p = endpath(draw=False) drawpath(p)
- autoclosepath(close=True)¶
Defines whether paths are automatically closed by connecting the last and first points with a line. It takes a single parameter of True or False. All shapes created with beginpath() following this command will adhere to the setting.
- findpath(points, curvature=1.0)¶
Constructs a fluid path from a list of coordinates. Each element in the list is a 2-tuple defining the x-coordinate and the y-coordinate. If the curve has more than three points, the curvature parameter offers some control on how separate segments are stitched together: from straight lines (0.0) to smooth curves (1.0).
points = [(10, 10), (90, 90), (350, 200)] ellipsemode(CENTER) for x, y in points: ellipse(x, y, 6, 6) nofill() stroke(0.2) autoclosepath(False) path = findpath(points) drawpath(path)
- beginclip(path)¶
- endclip()¶
The beginclip() and endclip() commands define a clipping mask. The supplied parameter defines the path to be used as a clipping mask.
All basic shapes and path commands return paths that can be used with beginclip() - setting the
draw
parameter of a shape command will simply return the path without actually drawing the shape. Any shapes, paths, texts and images between beginclip() and endclip() are clipped: any part that falls outside the clipping mask path is not drawn.p = ellipse(20, 20, 60, 60, draw=False) beginclip(p) stroke(0.5) strokewidth(15) line(20, 20, 80, 80) line(20, 80, 80, 20) line(50, 20, 50, 80) endclip()
Text¶
- text(txt, x, y, width=None, height=None, outline=False, draw=True)¶
Draws a string of text according to the current font settings.
This command takes 3 mandatory arguments: the string of text to write and the (x, y) coordinates of the baseline origin.
If
width
is set, the text will wrap (move to the next line) when it exceeds the specified value. Settingheight
will limit the vertical size of the text box, after which no text will be drawn.If the
outline
option is true, the resulting object will be a BezierPath instead of a Text object. It’s an alternative to usingtextpath()
.# when using text(), the origin point # is on the text baseline arrow(12, 65, 10, type=FORTYFIVE, fill='#ff9966') # place the text box font("Inconsolata", 50) text("Bot", 12, 65)
- font(fontpath=None, fontsize=None)¶
Sets the font to be used in new text instances. Accepts a system font name, e.g. “Inconsolata Bold”, and an optional font size value. Returns the current font name.
A full list of your system’s font names can be viewed with the
pango-list
command in a terminal.fill(0.3) fontsize(16) font("Liberation Mono") text("Bot", 35, 25) font("Liberation Mono Italic") text("Bot", 35, 45) font("Liberation Mono Bold") text("Bot", 35, 65) font("Liberation Mono Bold Italic") text("Bot", 35, 85)
Variable fonts are supported. You can specify the value for an axis using keyword arguments with the
var_
prefix: to set thewdth
axis to100
, usevar_wdth=100
.Alternatively, you can provide a
vars
dictionary with each axis’s values, e.g.font("Inconsolata", vars={"wdth": 100, "wght": 600})
fill(0.3) fontsize(30) for x, y in grid(5, 4, 20, 22): font("Inconsolata", var_wdth=y+50, var_wght=x*12) text("R", 3+x, 25+y)
Note that for the above example to work, you need to install the variable version of Inconsolata.
- fontsize(fontsize=None)¶
Sets the size of the current font to use, and returns the current size.
- textpath(txt, x, y, width=None, height=1000000, draw=False)¶
Returns an outlined path of the input text.
For an explanation of the parameters, see
text()
. Note that, unline text(), thedraw
option is False by default, as this command is meant for doing further manipulation on the text path before rendering it.
- textmetrics(txt, width=None, height=None)¶
Returns a (width, height) tuple with the dimensions of the text box containing a string of text, according to the current font settings.
- textbounds(txt, width=None, height=None)¶
Returns a (width, height) tuple with the dimensions of the actual shapes (inked part) of a string of text, according to the current font settings.
- textwidth(txt, width=None)¶
Accepts a string and returns its width, according to the current font settings.
- textheight(txt, width=None)¶
Accepts a string and returns its height, according to the current font settings.
- lineheight(height=None)¶
Set the space between lines of text.
- align(align=LEFT)¶
Set the way lines of text align with each other. Values can be LEFT, CENTER or RIGHT.
- fontoptions(hintstyle=None, hintmetrics=None, subpixelorder=None, antialias=None)¶
Sets text rendering options.
The
antialias
option specifies the type of antialiasing to do:default
– use the default antialiasing for the subsystem and target devicenone
– no antialiasinggray
– single-color antialiasingsubpixel
– take advantage of the order of subpixel elements on devices such as LCD panelsfast
– prefer speed over qualitygood
– balance quality against performancebest
– render at the highest quality, sacrificing speed if necessary
The
subpixelorder
sets the order to use with the antialiassubpixel
option:rgb
– arranged horizontally with red at the leftbgr
– arranged horizontally with blue at the leftvrgb
– arranged vertically with red at the topvbgr
– arranged vertically with blue at the top
The
hintstyle
option sets the amount of font hinting to apply:default
– use the default hint style for font backend and target devicenone
– do not hint outlinesslight
– improve contrast while retaining good fidelity to the original shapesmedium
– compromise between fidelity to the original shapes and contrastfull
– maximize contrast
The
hintmetrics
option (on
oroff
) deals with hint metrics, which means quantizing (or “rounding”) glyph outlines so that they are integer values. Doing this improves the consistency of letter and line spacing, but it also means that text will be laid out differently at different zoom factors.
Colors¶
Shapes can be filled, stroked, or both. The
fill()
andstroke()
commands are used to set the colors for those operations globally. In addition, most drawing commands havefill
andstroke
parameters to allow setting colors for single objects.Fill and stroke colors can be specified in a few ways:
grayscale:
(value)
and(value, alpha)
RGB:
(red, green, blue)
and(red, green, blue, alpha)
hex colors:
('#FFFFFF')
and('#FFFFFFFF')
Color objects as created by
color()
The grayscale and RGB options take values between 0 and 1; this behavior can be changed with
colorrange()
.
- background(*args)¶
Set the background color.
background(0.9) fill(1) circle(40, 40, 20)
- fill(color)¶
Sets a fill color, applying it to new paths.
- nofill()¶
Stop applying fills to new paths.
Returns the fill color that was active before the nofill() call.
- stroke(color)¶
Set a stroke color, applying it to new paths.
This command can be used without arguments, in which case it returns the current stroke color. When used to set a color, it returns the new color value.
- nostroke()¶
Stop applying strokes to new paths.
Returns the stroke color that was active before the nostroke() call.
- strokewidth(w=None)¶
Set the width of the stroke in new paths.
Returns the current stroke width.
stroke(0.2) strokewidth(1) line(20, 20, 20, 110) strokewidth(3) line(40, 20, 40, 110) strokewidth(10) line(60, 20, 60, 110) strokewidth(15) line(80, 20, 80, 110)
- strokedash(dashes, offset=0)¶
Sets a dash pattern to be used in stroked shapes.
A dash pattern is specified by dashes - a list of positive values. Each value provides the length of alternate “on” and “off” portions of the stroke.
The offset specifies an offset into the pattern at which the stroke begins.
Each “on” segment will have caps applied as if the segment were a separate sub-path. In particular, it is valid to use an “on” length of 0 with a round or square stroke cap (see
strokecap()
) in order to distribute dots or squares along a path.If the number of dashes is 0, dashing is disabled.
If the number of dashes is 1, a symmetric pattern is assumed with alternating on and off portions of the size specified by the single value in dashes.
nofill() stroke(0.2) strokewidth(3) circle(5,5,40) strokedash([3,2,1,2]) circle(55,5,40) strokedash([10,15,5]) circle(5,55,40) strokedash([10,15,5], 20) strokecap(ROUND) circle(55,55,40)
- strokecap(cap)¶
Sets the cap to be drawn at the ends of strokes.
This command can be called with a new cap value:
BUTT
– start/stop the line exactly at the start/end pointROUND
– use a round ending, the center of the circle is the end pointSQUARE
– use a squared ending, the center of the square is the end point
If called with no arguments, returns the current cap value.
stroke(0.2) strokewidth(15) line(25, 25, 25, 110) strokecap(ROUND) line(50, 25, 50, 110) strokecap(SQUARE) line(75, 25, 75, 110)
- strokejoin(join)¶
Sets the join shape to use be drawn at the ends of strokes.
This command can be called with a new join value:
MITER
– use a sharp angled corner (default)ROUND
– use a rounded join, the center of the circle is the joint pointBEVEL
– use a cut-off join, the join is cut off at half the line width from the joint point
If called with no arguments, returns the current join value.
autoclosepath(False) nofill() stroke(0.2) strokewidth(15) beginpath(10,25) lineto(40,50) lineto(10,75) endpath() translate(25,0) strokejoin(ROUND) beginpath(10,25) lineto(40,50) lineto(10,75) endpath() translate(25,0) strokejoin(BEVEL) beginpath(10,25) lineto(40,50) lineto(10,75) endpath()
- color(*args)¶
Returns a Color object that can be stored in a variable and reused.
teal = color("#008080") rect(20, 20, 60, 15, fill=teal) rect(20, 40, 60, 15, fill=teal) rect(20, 60, 60, 15)
- colormode(mode=None, crange=None)¶
Set the current color mode, which can be RGB or HSB, and optionally the color range.
- colorrange(crange=1.0)¶
Set the numeric range for color values. By default colors range from 0.0 - 1.0, and this command can set this to a different range. For example, a scale of 0 to 255 can be set with
colorrange(255)
.colorrange(255) background(127) fill(255) circle(40, 40, 20)
- blendmode(mode):
Sets the blending mode to apply to the colors of new elements.
Blending modes, also known as Porter-Duff compositing operations, are ways to combine two images. Usually, an image (destination) placed on top of another (destination) completely covers it; this is the OVER blending mode, but there are many others that give distinct results, and which you might know from image editors.
OVER
– draw source layer on top of destination layerMULTIPLY
– source and destination layers are multiplied. This causes the result to be at least as dark as the darker inputs.SCREEN
– source and destination are complemented and multiplied. This causes the result to be at least as light as the lighter inputs.OVERLAY
– multiplies or screens, depending on the lightness of the destination colorDARKEN
– replaces the destination with the source if it is darker, otherwise keeps the sourceLIGHTEN
– replaces the destination with the source if it is lighter, otherwise keeps the source.COLORDODGE
– brightens the destination color to reflect the source colorCOLORBURN
– darkens the destination color to reflect the source colorHARDLIGHT
– multiplies or screens, dependent on source colorSOFTLIGHT
– darkens or lightens, dependent on source colorDIFFERENCE
– takes the difference of the source and destination colorEXCLUSION
– produces an effect similar to difference, but with lower contrastHUE
– creates a color with the hue of the source and the saturation and luminosity of the targetSATURATION
– creates a color with the saturation of the source and the hue and luminosity of the target. Painting with this mode onto a gray area produces no change.COLOR
– creates a color with the hue and saturation of the source and the luminosity of the target. This preserves the gray levels of the target and is useful for coloring monochrome images or tinting color images.LUMINOSITY
– creates a color with the luminosity of the source and the hue and saturation of the target. This produces an inverse effect to COLOR.ATOP
– draw source on top of destination content and only thereDEST
– ignore the sourceDEST_OVER
– draw destination on top of sourceDEST_ATOP
– leave destination on top of source content and only thereXOR
– source and destination are shown where there is only one of themADD
– source and destination layers are accumulatedSATURATE
– like over, but assuming source and dest are disjoint geometries
The Wikipedia page on blending modes is a deeper reference on how these work, and the Cairo operators page is also a good resource.
- fillrule(rule=WINDING)¶
Sets the fill rule to be used in filled shapes.
The fill rule is used to determine which regions are inside or outside a complex (potentially self-intersecting) path.
Transforms¶
- transform(mode=None)¶
Sets whether shapes are transformed along their centerpoint or (0,0).
The mode parameter can be CORNER (default) or CENTER.
It sets the registration point – the offset for
rotate()
,scale()
andskew()
commands. By default, primitives, text, and images rotate around their own centerpoints. But if you call transform() with CORNER as its mode parameter, transformations will be applied relative to the canvas top left corner (its “origin point”) instead.See the examples in
translate()
,rotate()
,scale()
andskew()
to see how the transform mode affects the result.
- translate(xt, yt)¶
Specifies the amount to move the canvas origin point.
Once called, all commands following translate() are repositioned, which makes translate() useful for positioning whole compositions of multiple elements.
fill(0.2) oval(10, 10, 40, 40) translate(45, 45) oval(10, 10, 40, 40)
- rotate(degrees=0, radians=0)¶
Rotates all subsequent drawing commands.
The default unit is degrees; radians can be used with
rotate(radians=PI)
.This command works incrementally: if you call
rotate(30)
, and later on callrotate(60)
, all commands following that second rotate() will be rotated 90° (30+60).fill('#4a69bd', 0.2) translate(25, 25) for i in range(7): rotate(15) rect(0, 0, 50, 50)
fill('#e55039', 0.2) transform(CENTER) for i in range(5): rotate(15) rect(25, 25, 50, 50)
- scale(x=1, y=None)¶
Increases, decreases, or streches the size of all subsequent drawing commands.
The first parameter sets the horizontal scale and the optional second parameter the vertical scale. You can also call scale() with a single parameter that sets both the horizontal and vertical scale. Scale values are specified as floating-point (decimal) numbers with 1.0 corresponding to 100%.
This command works incrementally: if you call
scale(0.5)
, and later on callscale(0.2)
, all subsequent drawing commands will be sized to 10% (0.2 of 0.5).fill('#78e08f', 0.2) translate(25,25) for i in range(7): rect(0, 0, 50, 50) scale(.8)
fill('#60a3bc', 0.2) transform(CENTER) for i in range(7): rect(25, 25, 50, 50) scale(.8)
- skew(x=1, y=0)¶
Slants the direction of all subsequent drawing commands.
The first parameter sets the horizontal skew. The second parameter is optional and sets the vertical skew.
This command works incrementally: if you call
skew(10)
, and later on callskew(20)
, all subsequent drawing commands will be skewed by 30° (10+20).fill('#82ccdd', 0.2) translate(5, 25) for i in range(7): rect(0, 0, 50, 50) skew(.2, 0)
fill('#e58e26', 0.2) transform(CENTER) for i in range(7): rect(25, 25, 50, 50) skew(.2, 0)
- push()¶
Saves the current transform state.
The push() function, along with its companion pop(), allows for “saving” a transform state. All transformations, such as rotate() and skew(), defined between push() and pop() will stop being applied after pop() is called.
fill(0.2) fontsize(14) transform(CENTER) rotate(45) text("one", 40, 40) push() rotate(-45) text("two", 40, 80) pop() text("three", 40, 120)
- pop()¶
Restores the saved transform state.
This command is meant to be used after push(). It “loads” the transform state that was set before the call to push().
- reset()¶
Resets the transform state to its default values.
transform(CENTER) rotate(30) text("one", 10, 20) text("two", 10, 50) reset() text("three", 10, 80)
Images¶
- image(path, x=0, y=0, width=None, height=None, alpha=1.0, data=None, draw=True)¶
Place an image on the canvas with (x,y) as its top left corner. Both bitmap and SVG images can be used; in the case of SVG images, the result is rendered as paths (not bitmaps).
If
width
andheight
are specified, the image is resized to fit. Thealpha
parameter (0-1) controls the image opacity.A filename is expected, but you can use the
data
argument instead to pass image data as a string or file-like object.image("source/images/sign.jpg", 0, 0, 100, 100)
- imagesize(path)¶
Get the dimensions of an image file as a (width, height) tuple.
Utility¶
- var(name, type, default=None, min=0, max=255, value=None, step=None, steps=256.0)¶
Creates a live variable, which can be manipulated using the variables UI, socket server or live coding shell.
The first two arguments are the variable name and type (NUMBER, TEXT, BOOLEAN or BUTTON).
An optional third argument is the default (initial) value. For NUMBER variables, the minimum and maximum values for the variable can be indicated.
Finally, there are two options for setting the step length on the variables interface. This is the “jump” between values if you don’t want to use a continuous scale. You can either set a fixed number of steps using the
steps
option, or set a step length with thestep
option.
- random(v1=None, v2=None)¶
Returns a random number that can be assigned to a variable or a parameter. When no parameters are supplied, returns a floating-point (decimal) number between 0.0 and 1.0 (including 0.0 and 1.0). When one parameter is supplied, returns a number between 0 and this parameter. When two parameters are supplied, returns a number between the first and the second parameter.
r = random() # returns a float between 0 and 1 r = random(2.5) # returns a float between 0 and 2.5 r = random(-1.0, 1.0) # returns a float between -1.0 and 1.0 r = random(5) # returns an int between 0 and 5 r = random(1, 10) # returns an int between 1 and 10 # sets the fill to anything from # black (0.0,0,0) to red (1.0,0,0) fill(random(), 0, 0) circle(40, 40, 20) # Note: new random values are returned each time the script runs. # The variation can be locked by supplying a custom random seed: from random import seed seed(0)
- grid(cols, rows, colSize=1, rowSize=1, shuffled=False)¶
This command returns an iterable object which can be traversed in a loop.
The first two parameters define the number of columns and rows in the grid. The next two parameters are optional, and set the width and height of one cell in the grid. In each loop iteration, the offset for the current column and row is returned.
If
shuffled
is True, the cells will be returned in a random order.translate(10, 10) for x, y in grid(7, 5, 12, 12): rect(x, y, 10, 10)
- files(path='*')¶
Retrieves all files from a given path and returns their names as a list. Wildcards can be used to specify which files to pick, e.g.
f = files('*.gif')
- autotext(sourceFile)¶
Accepts a source file name, and generates mock philosophy based on a context-free grammar.
Core¶
- size(w, h)¶
Sets the size of the canvas. Only the first call will have any effect.
- speed(framerate)¶
Sets the frame rate for animations (frames per second), and returns the current frame rate.
- run(inputcode, iterations=None, run_forever=False, frame_limiter=False)¶
Executes the contents of a Shoebot script in the current surface’s context.
Shapes¶
Paths¶
Text¶
Colors¶
Transforms¶
Images¶
Utility¶
Core¶
Showcase¶
Nodebox demos¶
This is a selection of demos from Nodebox 1, by the Nodebox authors (Frederik de Bleser and Tom de Smedt), with slight edits.
Math sculpture¶
Generates sculptures using a set of mathematical functions. Every iteration adds a certain value to the current coordinates. Rewriting this program to use transforms is left as an exercise for the reader.

from math import sin, cos, log10
size(400, 800)
background(0)
cX = random(1, 10)
cY = random(1, 10)
x = 200
y = 54
fontsize(10)
for i in range(278):
x += cos(cY) * 11
y += log10(cX) * 1.85 + sin(cX) * 5
fill(random() - 0.4, 0.8, 0.8, random())
s = 10 + cos(cX) * 15
oval(x - s / 2, y - s / 2, s, s)
# Try the next line instead of the previous one to see how
# you can use other primitives.
# star(x-s/2, y-s/2, random(5, 10), inner=2+s*0.1, outer=10+s*0.1)
cX += random(0.25)
cY += random(0.25)
Ball grid¶
Use a grid to generate a bubble-like composition.
This example shows that a grid doesn’t have to be rigid at all. It’s very easy to break loose from the coordinates Shoebot passes you, as is shown here. The trick is to add or subtract something from the x and y values Shoebot passes on. Here, we also use random sizes.

from math import sin, cos
size(600, 600)
gridSize = 40
# Translate a bit to the right and a bit to the bottom to
# create a margin.
translate(100, 100)
startval = random()
c = random()
for x, y in grid(10, 10, gridSize, gridSize):
fill(sin(startval + y * x / 100.0), cos(c), cos(c), random())
s = random() * gridSize
oval(x, y, s, s)
fill(cos(startval + y * x / 100.0), cos(c), cos(c), random())
deltaX = (random() - 0.5) * 10
deltaY = (random() - 0.5) * 10
deltaS = (random() - 0.5) * 200
oval(x + deltaX, y + deltaY, deltaS, deltaS)
c += 0.01
Blines and circloids¶
by Tom de Smedt <https://www.nodebox.net/code/index.php/Blines_and_Circloids>
![# You'll need the Boids and Cornu libraries.
boids = ximport("boids")
cornu = ximport("cornu")
size(550, 550)
background(0.1, 0.1, 0.0)
nofill()
flock = boids.flock(10, 0, 0, WIDTH, HEIGHT)
n = 70
for i in range(n):
flock.update(shuffled=False)
# Each flying boid is a point.
points = []
for boid in flock:
points.append((boid.x, boid.y))
# Relativise points for Cornu.
for i in range(len(points)):
x, y = points[i]
x /= 1.0 * WIDTH
y /= 1.0 * HEIGHT
points[i] = (x, y)
t = float(i) / n
stroke(0.9, 0.9, 4 * t, 0.6 * t)
cornu.drawpath(points, tweaks=0)](_images/shoebot-f2157cbd77f0aa910e0f11d2921d8b7517de2a93.png)
# You'll need the Boids and Cornu libraries.
boids = ximport("boids")
cornu = ximport("cornu")
size(550, 550)
background(0.1, 0.1, 0.0)
nofill()
flock = boids.flock(10, 0, 0, WIDTH, HEIGHT)
n = 70
for i in range(n):
flock.update(shuffled=False)
# Each flying boid is a point.
points = []
for boid in flock:
points.append((boid.x, boid.y))
# Relativise points for Cornu.
for i in range(len(points)):
x, y = points[i]
x /= 1.0 * WIDTH
y /= 1.0 * HEIGHT
points[i] = (x, y)
t = float(i) / n
stroke(0.9, 0.9, 4 * t, 0.6 * t)
cornu.drawpath(points, tweaks=0)
Color grid¶
This example showcases the HSB color mode to select colors more naturally, by specifying a hue, saturation and brightness.

size(625, 625)
colormode(HSB)
# Set some initial values. You can and should play around with these.
h = 0
s = 0.5
b = 0.9
a = 0.5
# Size is the size of one grid square.
square_size = 50
# Using the translate command, we can give the grid some margin.
translate(50, 50)
# Create a grid with 10 rows and 10 columns. The width of the columns
# and the height of the rows is defined in the 'size' variable.
for x, y in grid(10, 10, square_size, square_size):
# Increase the hue while choosing a random saturation.
# Try experimenting here, like decreasing the brightness while
# changing the alpha value etc.
h += 0.01
s = random()
# Set this to be the current fill color.
fill(h, s, b, a)
# Draw a rectangle that is one and a half times larger than the
# grid size to get an overlap.
rect(x, y, square_size * 1.5, square_size * 1.5)
Circles and Beziers¶

size(640, 400)
colorrange(255)
colormode(HSB)
background(0, 0, 192)
for i in range(0, WIDTH // 4, 1):
# ^ range requires a float, so use integer division '//'
sz = random(WIDTH / 40, WIDTH / 5)
xpos = random(-WIDTH / 5, WIDTH)
ypos = random(-WIDTH / 5, HEIGHT)
fill(0, 0, random(192, 224), random(85, 255))
ellipse(xpos, ypos, sz, sz)
nofill()
stroke(0, 0, 0, 255)
strokewidth(.1)
ellipse(xpos, ypos, sz, sz)
for j in range(0, WIDTH // 80, 1):
# ^ range requires a float, so use integer division '//'
stroke(0, 0, 0, 255)
strokewidth(.1)
beginpath(random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5))
curveto(random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5),
random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5),
random(-WIDTH / 2, WIDTH * 1.5), random(-HEIGHT / 2, HEIGHT * 1.5))
endpath()
Libraries¶
Shoebot has a rich set of external libraries to enable new tools and commands.
Colors¶
The Colors library offers a set of tools to work with color more conveniently.
You should read the original Nodebox documentation for the Colors library. The below parts are a work in progress port of the Nodebox docs.
You can use the library to create colors by name (like red or ivory), from pixels in an image, group them into lists of which you can then collectively manipulate hue, brightness and saturation, create lists of harmonious colors based on color theory rules (like complementary or analogous), create lists of gradient colors, work with drop shadows and gradient fills for paths, define powerful indefinite color ranges (like bright red or purplishgreenish) and more.
How to get the library up and running¶
Just place this line at the start of your script:
colors = ximport("colors")
Outside of Shoebot you can also just import colors
. Color lists from image
pixels then work with PIL/Pillow.
(…)
Colors¶
The Colors library has a number of commands that create a new color you can use with fill() or stroke().
rgb(r, g, b, a=None, range=1.0, name="")
hsb(h, s, b, a=None, range=1.0, name="")
cmyk(c, m, y, k, range=1.0, name="")
lab(l, a, b, range=1.0, name="")
hex(str, name="")
With the range parameter you can define how you want to supply the channel values. For example, if you want to define r, g and b between 0 and 255 instead of between 0.0 and 1.0, set range to 255.
The optional name parameter lets you define a name for the color. Otherwise, a name will be guessed using the clr.nearest_hue() method (see below).
The hex() command creates a color from a hexadecimal string (e.g. “#30343D”).
The named_color() command creates a color from a name like “olive” or “maroon” or “antiquewhite”. A list of all the named colors the command will recognize is here. The really great thing is that each of these named colors is also a command in the Colors library. So the two colors in the example below are exactly the same:
clr1 = colors.named_color("olive")
clr2 = colors.olive()
Color properties¶
Each of the above commands returns a Color object. It has all the standard properties a color created with the NodeBox color() command also has. You can use these to find out the color’s R, B and B values, or its C, M, Y and K values, or its H, S and B values:
clr.r: the red value in RGB.
clr.g: the green value in RGB.
clr.b: the blue value in RGB.
clr.a: the alpha value (opacity).
clr.c: the cyan value in CMYK.
clr.m: the magenta value in CMYK.
clr.y: the yellow value in CMYK.
clr.k: the black value in CMYK.
clr.hue: the hue of the color in HSB.
clr.saturation: the saturation (grayness) of the color in HSB.
clr.brightness: the brightness of the color in HSB.
The Color object in the Colors Library has some additional properties:
clr.name: the name of this color.
clr.is_black: will be True when the color’s R, G and B values are 0.
clr.is_white: will be True when the color’s R, G and B values are 1.
clr.is_gray: will be True when the color’s R, G and B values are the same.
clr.is_transparent: will be True when the color is completely transparent.
clr.complement: the complementary color (i.e. 180 degrees across the color wheel) of this color.
Color list math¶
Individual colors (or lists of colors) can be added to a list with the + operator:

clrs = colors.list(
colors.purple().darken(),
colors.deeppink()
)
clrs += colors.violet()
clrs.swarm(50, 50)
Also, the *
operator is equivalent to the list.repeat()
method.
Bezier¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the Bezier library,
Cornu¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the Cornu library,
SVG¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the SVG library,
Graph¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the Graph library,
Photobot¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the Photobot library,
Boids¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the Boids library, since it should work just the same on Shoebot.
If it does not, please let us know by making a new issue on the Shoebot repository.
L-System¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the L-System library,
Audio¶
Grab audio frequencies to create visualizations.
Import it with ximport("sbaudio")
.
Requires the pysoundcard
Python module to work.
- fft_bandpassfilter(data, fs, lowcut, highcut)¶
- flatten_fft(scale=1.0)¶
- scaled_fft(fft, scale=1.0)¶
- triple(spectrogram)¶
- fuzzydevices(match='', min_ratio=30)¶
- firstfuzzydevice(match='')¶
Video¶
This is a very basic video library. The library itself is built upon OpenCV, a library of programming functions mainly aimed at real time computer vision.
You need opencv
and its python bindings in order to use this library, so on
debian-based Linux you will have to install python-opencv
and
libhighgui1
or similar.
The Video Library is meant to grab frames from webcams, surveillance cameras or video files, and to make them available to Shoebot for displaying and elaboration.
For cameras, two camera interfaces can be used on Windows: Video for Windows (VFW) and Matrox Imaging Library (MIL). On Linux, we can use V4L and FireWire (IEEE1394).
For video files, this library uses specific backends on each OS:
ffmpeg on Linux
Video for Windoes (VfW) on Windows
QuickTime on Mac OS X
Up to now only camera and video frame grabbing is supported, but future development could introduce features like face and object recognition or video output to file for Shoebot animation. Future development will depend on evolution of the SWIG python bindings of OpenCV, which at present is still in-progress.
Basic usage¶
First, import the library:
videolib = ximport("sbvideo")
Then you can create two different types of capture devices:
# for video files
video = videolib.movie(file_path)
# or for webcam
camera = videolib.camera(index, width, height)
If you have only one camera, you can omit index
and the first camera should
be picked. Parameters width
and height
are optional, and they’re not
guaranteed to work with your camera. Now you can grab a video frame with:
frame = video.frame() or frame = camera.frame()
frame has some properties: frame.width frame.height frame.time # strange results at present frame.data
You need frame.data
in order to pass your frame image to the Shoebot canvas
using the image()
command:
image(None, xpos, ypos, data=frame.data)
You can manipulate and use the image as any other image in Shoebot.
When you call the video-file capture constructor, you’re supposed to be able to set a starting point in seconds from file beginning, but at present this feature is disabled as I could not get it to work properly on my linux system If you want to test it you can uncomment the relative lines in SVL. (If you succeed, please report it to the Shoebot issue tracker).
Commands¶
- sbvideo.movie(path, start=0, stop=None)¶
- sbvideo.camera(cam=0, width=None, height=None)¶
OpenCV¶
- sbopencv.movie(path, start=0, stop=None)¶
- sbopencv.camera(cam=0, width=None, height=None)¶
- sbopencv.image(path=None)¶
- sbopencv.ipl2cairo(iplimage)¶
- sbopencv.detectHaar(iplimage, classifier)¶
- sbopencv.findcontours(iplimage, threshold=100)¶
Database¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the Database library,
Bezier Editor¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the Bezier Editor library,
TUIO¶
We’re still in the process of porting the Nodebox library documentation to here.
In the meantime, read the original Nodebox documentation for the TUIO library,
Live Variables¶
Shoebot is not limited to what you write inside your .bot file; you can open your sketch for receiving and reacting to data from outside applications and environments.
These can be created using the var()
command.
Using the live variables GUI¶
When a script uses the var keyword, the corresponding widget will appear in the live variables GUI.
The following code will make a slider with a minimum value of 0, a maximum value of 100, and an initial (default) value of 25.
var('a_number', NUMBER, 25., 0., 100.)

For readability names are capitalized and underscores replaced by spaces, so ‘a_number’ is displayed as ‘A number’.
Setting variables in the command line¶
Live variables can be set from the commandline using JSON syntax:
$ sbot --vars='{ "hue": 32 }' examples/vars/circle_circle.bot
This means that a Shoebot script can be called with different parameters without
having to change the script itself – just specify the live variable initial
values with --vars
.
Socket server¶
If shoebot is run with the –serverport option, a socket server will also be started. Socket servers can accept connections from other applications or even across the network in order to set variables inside running Shoebot scripts.
$ sbot -s examples/animation/hypnoval.bot
Listening on port 7777...
Once it’s running, it’s easy to connect with telnet:
$ telnet 127.0.0.1 7777
This gets you into the shell, where you can use the commands below to list and set variables, rewind and go to frames.
The following commands can also be sent through other applications like Pure Data; there is a simple PD example in examples/socketcontrol/helloworld.pd
. Be sure to take a look at the socket server examples in action inside the examples/socketserver directory.
Socketserver commands¶
Command
Description
goto 100
go to frame 100
pause
pause playback
rewind
set FRAME to 0
restart
set FRAME to 0 and reset all variables
vars
show content of all live variables
set n=1
set variable ‘n’ to value 1
n=1
set variable ‘n’ to value 1
help
show list of all commands
Using the live shell¶
Shoebot provides a live shell for communication with text editors, as well as livecoding.
All the socket server commands above are available, along with load_base64
which allows livecoding.
To experiment with the shell, run an example with the -l
option:
$ sbot -l examples/animation/hypnoval.bot
The shell accepts all the socket server commands, along with a couple more; these can be useful for an editor or IDE.
Live Shell commands¶
Command
Description
quit
quit shoebot
load_base64
used by IDE/Editor to send new code to Shoebot
Troubleshooting¶
Here you’ll find help with some common problems. If your problem isn’t listed,
feel free to file an issue
including your error message and the output of python setup.py diagnose
. You
can also join us on the Shoebot Matrix channel.
Installation issues¶
Check progress with diagnose¶
If you’re having trouble with a specific package, Shoebot provides a ‘diagnose’
command to check if things are working: python3 setup.py diagnose
It’s usually easiest to start with Python3 and Pycairo, then move on to PyGobject, Pango and Gtk3.
Try PGI with CairoCFFI and GTK3 instead of PyGobject and Pycairo¶
Shoebot can run under PGI and CairoCFFI, which may be easier to install than the recommended setup with Pygobject and Pycairo.
In this setup Shoebot can work with the GUI, but text output is not available.
Other problems¶
TypeError: Couldn’t find foreign struct converter for ‘cairo.Context’¶
If you see this error, it means you’re missing the Python GObject interface for cairo. On Debian/Ubuntu, this should be fixed with:
sudo apt-get install python3-gi-cairo
See the installation page to know the relevant dependencies in other distros.
The Gedit plugin does not activate¶
Try running Gedit from the command line so that you can see debug messages. If you see one of these warnings:
** (gedit:3830): WARNING **: Could not load Gedit repository: Typelib file for namespace 'GtkSource', version '3.0' not found
or:
ImportError: cannot import name Gedit
then try installing the gir1.2-gtksource-3.0
package.
This StackOverflow answer helped on finding this solution.
Using with other tools¶
This section is aimed at Python dabblers and hackers who want to get into the more involved features of Shoebot.
Using Shoebot as a Python module¶
Shoebot can be easily loaded as a module inside a Python script.
import shoebot
# set up a canvas
bot = shoebot.create_bot(outputfile="output.svg")
# size() needs to be called first
bot.size(400,400)
# now we can draw!
bot.rect(10,10,100,100)
# finish() should be called after all drawing commands
bot.finish()
Take a snapshot of the current state:
bot.snapshot("snap.png")
Run a Shoebot/Nodebox script:
bot.run("example.bot")
Running in Jupyter¶
Jupyter notebooks <https://jupyter.org>_ are fantastic, and Shoebot runs pretty well inside them!
First, you need to have Jupyter installed, as well as the development version
of Shoebot. Using virtualenvwrapper
for this is heavily recommended.
# create the virtualenv
mkvirtualenv jupytershoebot -p $(which python3)
# install jupyter dependencies
pip3 install jupyter jupyter-pip
# clone the Shoebot repository, enter it and install
git clone https://github.com/shoebot/shoebot
cd shoebot
python3 setup.py install
After ensuring both packages are available, install the extension after cloning the jupyter-shoebot repository:
# leave the shoebot/ dir
cd ..
# clone the jupyter-shoebot repository, enter it and install
git clone https://github.com/shoebot/jupyter-shoebot
cd jupyter-shoebot
python3 setup.py install
And finally, while still on the jupyter-shoebot/
directory, run
jupyter kernelspec install shoebot_kernel --sys-prefix
All done! Now you can run jupyter notebook
, go to the Kernel
menu, select
Change kernel
and select Shoebot
.
Be sure to try the notebook examples <https://github.com/shoebot/jupyter-shoebot/tree/master/example-notebooks>_ in the Jupyter Shoebot repository.
Running with PyPy¶
To get better performance, you can run Shoebot using PyPy3, which is experimental.
When installing Shoebot, you have to point to PyPy3 when creating your virtualenv. Instead of the first command in the Virtualenvwrapper install example, do:
mkvirtualenv shoebot -p $(which pypy3)
For the plain virtualenv approach, try:
virtualenv .env -p $(which pypy3)
Using with Django¶
See the shoebot-django for an example of integrating Shoebot into a Django application.
Extensions¶
There are a few plug-ins and extensions that make for a much better experience.
Atom extension¶

Atom is an excellent free software code editor, and there is a Shoebot package that turns it into the ideal tool to experiment with Shoebot.
Install it as you would any other Atom package: go to Preferences, Install and search for the “shoebot” package. Once installed, you get these extra commands:
Run (
Ctrl-Alt-R
): Run the open sketch on a windowExport (
Ctrl-Alt-E
): Run and save the output to a file (SVG, PNG or PDF)Export video: Run and save to an MP4 file
Be sure to check the extension options to set the location of your Shoebot installation.
Gedit plugin¶
This Gedit plugin adds a menu to allow for running Shoebot scripts. It will open a Shoebot window using the current document as the Shoebot code.
Run the following commands to add the plugin to Gedit:
cd shoebot/extensions/gedit
python3 install.py
Now restart Gedit. Navigate to Edit > Preferences > Plugins
and activate the
Shoebotit plugin.
TODO: document plugin usage
Command-line options¶
This is a list of all the flags you can specify to the sbot
command-line runner.
- --outputfile <FILENAME>, -o <FILENAME>
Run in headless mode, outputting the result directly to a file.
Supported extensions are
.png
,.svg
,.pdf
and.ps
.
- --socketserver, -s
Run a socket server for controlling live variables from other programs.
- --serverport <PORT>, -s <PORT>
Set the socket server port to listen for connections (default is 7777).
- --vars <VARS>, -v <VARS>
Initial live variables, in JSON format.
Use single quotes outside, and double quotes inside, e.g.:
--vars='{"variable1": 1}'
- --namespace <NAMESPACE>, -ns <NAMESPACE>
Initial namespace, in JSON format.
Use single quotes outside, and double quotes inside, e.g.:
--namespace='{"variable1": 1}'
TODO: Document the difference between –namespace and –vars
- --l, -l
Open a shell to control the bot as it runs. See the Shell mode documentation for the available commands.
- --repeat <TIMES>, -r <TIMES>
Set number of iterations to run the script, producing multiple images.
- --window, -w
Run the script in a GTK window (default).
- --fullscreen, -f
Run the script in fullscreen mode.
- --title <TITLE>, -t <TITLE>
Set the window title.
- --close, -c
Close the window after running the script. Use with
--repeat
for benchmarking.
- --disable-vars, -dv
Disable the variables pane when in windowed mode.
- --disable-background-thread, -dt
Don’t run code in a background thread. This option is only useful if running on OSX turns up issues.
- --verbose, -V
Show internal error information in tracebacks.
- --args <ARGS>, -a <ARGS>
Pass arguments to bot. [TODO: explain better]
API Reference¶
This is the technical descriptions of Shoebot’s objects and commands.
Path¶
- class shoebot.data.BezierPath(bot, path=None, fill=None, fillrule=None, stroke=None, strokewidth=0, strokecap=None, strokejoin=None, strokedash=None, dashoffset=None, blendmode=None, packed_elements=None)¶
Represents a Bezier path as a list of PathElements.
Shoebot implementation of Nodebox’s BezierPath wrapper. While Nodebox relies on Cocoa/QT for its data structures, this is more of an “agnostic” implementation that won’t require any other back-ends to do some simple work with paths.
(this last sentence is not so correct: we use a bit of Cairo for getting path dimensions)
- append(*args)¶
- addpoint(*args)¶
- copy()¶
- moveto(x, y)¶
- relmoveto(x, y)¶
- lineto(x, y)¶
- rellineto(x, y)¶
- line(x1, y1, x2, y2)¶
- curveto(x1, y1, x2, y2, x3, y3)¶
- relcurveto(x1, y1, x2, y2, x3, y3)¶
- arc(x, y, radius, angle1, angle2)¶
- closepath()¶
- ellipse(x, y, w, h, ellipsemode='corner')¶
- rect(x, y, w, h, roundness=0.0, rectmode='corner')¶
- contains(x, y)¶
Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.
- property center¶
Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.
- draw()¶
- point(t, segments=None)¶
Returns the PathElement at time t (0.0-1.0) on the path.
Returns coordinates for point at t on the path. Gets the length of the path, based on the length of each curve and line in the path. Determines in what segment t falls. Gets the point on that segment. When you supply the list of segment lengths yourself, as returned from length(path, segmented=True), point() works about thirty times faster in a for-loop since it doesn’t need to recalculate the length during each iteration.
- points(amount=100, start=0.0, end=1.0, segments=None)¶
Returns an iterator with a list of calculated points for the path. To omit the last point on closed paths: end=1-1.0/amount
- extend(pathelements)¶
- property bounds¶
Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.
- property contours¶
Returns a list of contours in the path, as BezierPath objects. A contour is a sequence of lines and curves separated from the next contour by a MOVETO. For example, the glyph “o” has two contours: the inner circle and the outer circle.
- property length¶
Returns the length of the path. Calculates the length of each spline in the path, using n as a number of points to measure. When segmented is True, returns a list containing the individual length of each spline as values between 0.0 and 1.0, defining the relative length of each spline in relation to the total path length.
- property fill¶
- inheritFromContext(ignore=())¶
Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.
- property stroke¶
- property strokewidth¶
PathElement¶
- class shoebot.data.PathElement(cmd=None, *args)¶
Represents a single element in a Bezier path.
The first argument should be a command string, following the proper values according to which element we want.
- Possible input:
(‘moveto’, x, y) (‘lineto’, x, y) (‘rlineto’, x, y) (‘curveto’, c1x, c1y, c2x, c2y, x, y) (‘rcurveto’, c1x, c1y, c2x, c2y, x, y) (‘arc’, x, y, radius, angle1, angle2) (‘ellipse’, x, y, w, h) (‘close’,)
Mind the trailing comma in the ‘close’ example, since it just needs an argument. The trailing comma is a way to tell python this really is supposed to be a tuple.
- set_ctrl1(ctrl1)¶
- get_ctrl1()¶
- set_ctrl2(ctrl2)¶
- get_ctrl2()¶
- property ctrl1¶
- property ctrl2¶
Image¶
- class shoebot.data.Image(bot, path=None, x=0, y=0, width=None, height=None, alpha=1.0, data=None, **kwargs)¶
- draw()¶
- property fill¶
- inheritFromContext(ignore=())¶
Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.
- property stroke¶
- property strokewidth¶
- property center¶
Returns the center point of the path, disregarding transforms.
- copy()¶
Color¶
- class shoebot.data.Color(*args, **kwargs)¶
Represents a single color.
Attributes (RGB and HSL) are values between 0 and 1
This stores color values as a list of 4 floats (RGBA) in a 0-1 range.
The value can come in the following flavours: - v - (v) - (v,a) - (r,g,b) - (r,g,b,a) - #RRGGBB - RRGGBB - #RRGGBBAA - RRGGBBAA
- property red¶
- property green¶
- property blue¶
- property alpha¶
- property data¶
- copy()¶
Clipping¶
- class shoebot.data.ClippingPath(bot, path=None, **kwargs)¶
- addpoint(*args)¶
- append(*args)¶
- arc(x, y, radius, angle1, angle2)¶
- property bounds¶
Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.
- property center¶
Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.
- closepath()¶
- contains(x, y)¶
Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached.
- property contours¶
Returns a list of contours in the path, as BezierPath objects. A contour is a sequence of lines and curves separated from the next contour by a MOVETO. For example, the glyph “o” has two contours: the inner circle and the outer circle.
- copy()¶
- curveto(x1, y1, x2, y2, x3, y3)¶
- draw()¶
- ellipse(x, y, w, h, ellipsemode='corner')¶
- extend(pathelements)¶
- property fill¶
- inheritFromContext(ignore=())¶
Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.
- property length¶
Returns the length of the path. Calculates the length of each spline in the path, using n as a number of points to measure. When segmented is True, returns a list containing the individual length of each spline as values between 0.0 and 1.0, defining the relative length of each spline in relation to the total path length.
- line(x1, y1, x2, y2)¶
- lineto(x, y)¶
- moveto(x, y)¶
- point(t, segments=None)¶
Returns the PathElement at time t (0.0-1.0) on the path.
Returns coordinates for point at t on the path. Gets the length of the path, based on the length of each curve and line in the path. Determines in what segment t falls. Gets the point on that segment. When you supply the list of segment lengths yourself, as returned from length(path, segmented=True), point() works about thirty times faster in a for-loop since it doesn’t need to recalculate the length during each iteration.
- points(amount=100, start=0.0, end=1.0, segments=None)¶
Returns an iterator with a list of calculated points for the path. To omit the last point on closed paths: end=1-1.0/amount
- rect(x, y, w, h, roundness=0.0, rectmode='corner')¶
- relcurveto(x1, y1, x2, y2, x3, y3)¶
- rellineto(x, y)¶
- relmoveto(x, y)¶
- property stroke¶
- property strokewidth¶
Text¶
- class shoebot.data.Text(bot, text, x=0, y=0, width=None, height=None, outline=False, ctx=None, draw=True, **kwargs)¶
- Changes from Nodebox 1:
font in Nodebox is a native Cocoa font, here it is the font name. _fontsize, _fontsize, _lineheight, _align in Nodebox are public fields.
Implementation of fonts uses Pango instead of Cocoa.
- property baseline¶
- property metrics¶
- property bounds¶
- Returns
TextBounds namedtuple containing bounds as (x, y, width, height)
- property path¶
- property center¶
Returns the center point of the path, disregarding transforms.
- copy()¶
- property fill¶
- inheritFromContext(ignore=())¶
Doesn’t store exactly the same items as Nodebox for ease of implementation, it has enough to get the Nodebox Dentrite example working.
- property stroke¶
- property strokewidth¶
Point¶
- class shoebot.data.Point(*args)¶
Taken from Nodebox and modified
Bot¶
- class shoebot.grammar.NodeBot(canvas=None, namespace=None, vars=None)¶
- color_mode = 'rgb'¶
- color_range = 1¶
- image(path, x, y, width=None, height=None, alpha=1.0, data=None, draw=True, **kwargs)¶
Draws a image with (x,y) as the top left corner.
If width and height are specified, resize the image to fit.
form path, in x,y and resize it to width, height dimensions.
- Parameters
path (filename) – location of the image on disk
x (float) – x-coordinate of the top left corner
y (float) – y-coordinate of the top left corner
width (float or None) – image width (leave blank to use its original width)
height (float or None) – image height (leave blank to use its original height)
alpha (float) – opacity
data (binary data) – image data to load. Use this instead of
path
if you want to load an image from memory or have another source (e.g. using the web library)draw (bool) – whether to place the image immediately on the canvas or not
- imagesize(path)¶
- Parameters
path – Path to image file.
- Returns
image size as a (width, height) tuple
- rect(x, y, width, height, roundness=0.0, draw=True, **kwargs)¶
Draw a rectangle.
- Parameters
x – top left x-coordinate
y – top left y-coordinate
width – rectangle width
height – rectangle height
roundness – rounded corner radius
draw (boolean) – whether to draw the shape on the canvas or not
fill – fill color
- Returns
BezierPath representing the rectangle
- rectmode(mode=None)¶
Get or set the current rectmode.
- Parameters
mode (CORNER, CENTER or CORNERS) – the mode to draw new rectangles in
- Returns
current rectmode value
- ellipse(x, y, width, height, draw=True, **kwargs)¶
Draw an ellipse.
- Parameters
x – top left x-coordinate
y – top left y-coordinate
width – ellipse width
height – ellipse height
draw (boolean) – whether to draw the shape on the canvas or not
- Returns
BezierPath representing the ellipse
- oval(x, y, width, height, draw=True, **kwargs)¶
Draw an ellipse.
- Parameters
x – top left x-coordinate
y – top left y-coordinate
width – ellipse width
height – ellipse height
draw (boolean) – whether to draw the shape on the canvas or not
- Returns
BezierPath representing the ellipse
- circle(x, y, diameter, draw=True, **kwargs)¶
Draw a circle :param x: x-coordinate of the top left corner :param y: y-coordinate of the top left corner :param diameter: circle diameter :param boolean draw: whether to draw the shape on the canvas or not :return: BezierPath representing the circle
- ellipsemode(mode=None)¶
Set the current ellipse drawing mode.
- Parameters
mode – CORNER, CENTER, CORNERS
- Returns
ellipsemode if mode is None or valid.
- line(x1, y1, x2, y2, draw=True, **kwargs)¶
Draw a line from (x1,y1) to (x2,y2).
- Parameters
x1 – x-coordinate of the first point
y1 – y-coordinate of the first point
x2 – x-coordinate of the second point
y2 – y-coordinate of the second point
draw (boolean) – whether to draw the shape on the canvas or not
- Returns
BezierPath representing the line
- arc(x, y, radius, angle1, angle2, type='chord', draw=True, **kwargs)¶
Draw an arc with center (x,y) between two angles in degrees.
- Parameters
x1 – start x-coordinate
y1 – start y-coordinate
radius – arc radius
angle1 – start angle
angle2 – end angle
- arrow(x, y, width, type='1', draw=True, **kwargs)¶
Draw an arrow.
- Parameters
x – arrow tip x-coordinate
y – arrow tip y-coordinate
width – arrow width (also sets height)
type (NORMAL or FORTYFIVE) – arrow type
draw (boolean) – whether to draw the shape on the canvas or not
- Returns
BezierPath object representing the arrow
- star(x, y, points=20, outer=100, inner=50, draw=True, **kwargs)¶
Draws a star.
- Parameters
x – center x-coordinate
y – center y-coordinate
points – amount of points
outer – outer radius
inner – inner radius
draw (boolean) – whether to draw the shape on the canvas or not
- beginpath(x=None, y=None, **kwargs)¶
Start a new Bézier path.
This command is needed before any other path drawing commands.
- Parameters
x (float or None) – x-coordinate of the starting point
y (float or None) – y-coordinate of the starting point
- moveto(x, y)¶
Move the Bézier “pen” to the specified point without drawing.
- Parameters
x (float) – x-coordinate of the point to move to
y (float) – y-coordinate of the point to move to
- lineto(x, y)¶
Draw a line from the pen’s current point.
- Parameters
x (float) – x-coordinate of the point to draw to
y (float) – y-coordinate of the point to draw to
- curveto(x1, y1, x2, y2, x3, y3)¶
- arcto(x, y, radius, angle1, angle2)¶
- closepath()¶
- endpath(draw=True, closed=None)¶
- drawpath(path, **kwargs)¶
- drawimage(image, x=None, y=None)¶
- Parameters
image – Image to draw
x – optional, x coordinate (default is image.x)
y – optional, y coordinate (default is image.y)
- Returns
- autoclosepath(close=True)¶
- relmoveto(x, y)¶
Move relatively to the last point.
- rellineto(x, y)¶
Draw a line using relative coordinates.
- relcurveto(h1x, h1y, h2x, h2y, x, y)¶
Draws a curve relatively to the last point.
- findpath(points, curvature=1.0)¶
Constructs a path between the given list of points.
Interpolates the list of points and determines a smooth bezier path betweem them.
The curvature parameter offers some control on how separate segments are stitched together: from straight angles to smooth curves. Curvature is only useful if the path has more than three points.
- beginclip(path)¶
Use a path as a clipping mask.
All drawing commands between beginclip() and endclip() will be drawn inside the clipping mask set by beginclip().
- Parameters
path (BezierPath) – the path to be used as a clipping mask
- endclip()¶
Finish a clipping mask and render the result.
- transform(mode=None)¶
Set the current transform mode.
- Parameters
mode (CORNER or CENTER) – the mode to base new transformations on
- translate(xt, yt)¶
Translate the canvas origin point by (xt, yt).
- Parameters
xt – Amount to move horizontally
yt – Amount to move vertically
- rotate(degrees=0, radians=0)¶
Set the current rotation in degrees or radians.
- Parameters
degrees – Degrees to rotate
radians – Radians to rotate
- scale(x=1, y=None)¶
Set a scale at which to draw objects.
1.0 draws objects at their natural size.
- Parameters
x – Scale on the horizontal plane
y – Scale on the vertical plane
- skew(x=1, y=0)¶
- push()¶
- pop()¶
- reset()¶
- outputmode()¶
NOT IMPLEMENTED
- colormode(mode=None, range=None)¶
Set the current colormode (can be RGB or HSB) and eventually the color range.
If called without arguments, it returns the current colormode.
- Parameters
mode (RGB or HSB) – Color mode to use
crange (float) – Maximum value for the new color range to use
- Returns
Current color mode
- colorrange(crange=None)¶
Sets the current color range.
The default is 0-1; for a range of 0-255, use
colorrange(256)
.- Parameters
crange (float) – maximum value for new color range
- Returns
current range value
- fill(*args)¶
Sets a fill color, applying it to new paths.
- Parameters
args – color in supported format
- nofill()¶
Stop applying fills to new paths.
- Returns
fill color before nofill() was called
- fillrule(r=None)¶
Set the fill rule to use in new paths.
- Parameters
r (WINDING or EVENODD) – fill rule to apply
- Returns
current fill rule value
- stroke(*args)¶
Set a stroke color, applying it to new paths.
- Parameters
args – color in supported format
- Returns
new stroke color
- nostroke()¶
Stop applying strokes to new paths.
- Returns
stroke color before nostroke() was called
- strokewidth(w=None)¶
Set the stroke width to be used by stroke().
- Parameters
w – width of the stroke to use
- Returns
current stroke width value
- strokedash(dashes=None, offset=0)¶
Sets the dash pattern to be used by stroke().
- Parameters
dashes (list) – a sequence specifying alternate lengths of on and off stroke portions
offset (float) – an offset into the dash pattern at which the stroke should start
- Returns
tuple with dashes value and offset
- strokecap(cap=None)¶
Set the stroke cap.
- Parameters
w – new stroke cap value
- Returns
current stroke cap value
- strokejoin(join=None)¶
Set the stroke join.
- Parameters
w – new stroke join value
- Returns
current line join value
- background(*args)¶
Set the canvas background color.
- Parameters
color – background color to apply
- Returns
new background color
- blendmode(mode=None)¶
Set the current blending mode.
- Parameters
mode – mode name (e.g. “multiply”)
- font(fontpath=None, fontsize=None, vars=None, *args, **kwargs)¶
Set the font to be used with new text instances.
- Parameters
fontpath – font name (can include styles like “Bold”)
fontsize – font size
vars – font variant values, as a dict of axis/value pairs (variable fonts only)
var_XXXX – set variant value (variable fonts only)
- Returns
current fontpath (if fontpath param not set)
Accepts TrueType and OpenType files. Depends on FreeType being installed.
- fontsize(fontsize=None)¶
Sets and/or returns the current font size.
- Parameters
fontsize – Font size in pt
- Returns
the current font size value
- text(txt, x, y, width=None, height=1000000, outline=False, draw=True, **kwargs)¶
Draws a string of text according to the current font settings.
- Parameters
txt – Text to output
x – x-coordinate of the top left corner
y – y-coordinate of the top left corner
width – text width
height – text height
outline – If True, draws a path instead of a text object (defaults to False)
draw – Set to False to inhibit immediate drawing (defaults to True)
- Returns
Path object representing the text.
- textpath(txt, x, y, width=None, height=1000000, draw=False, **kwargs)¶
Generates an outlined path of the input text.
- Parameters
txt – Text to output
x – x-coordinate of the top left corner
y – y-coordinate of the top left corner
width – text width
height – text height
draw – Set to False to inhibit immediate drawing (defaults to False)
- Returns
BezierPath representing the text
- textmetrics(txt, width=None, height=None, **kwargs)¶
Returns the dimensions of the text box of a string of text, according to the current font settings.
- Returns
(width, height) tuple
- textbounds(txt, width=None, height=None, **kwargs)¶
Returns the dimensions of the actual shapes (inked part) of a string of text, according to the current font settings.
- Returns
(width, height) tuple
- textwidth(txt, width=None, **kwargs)¶
Returns the width of a string of text according to the current font settings.
- Returns
- textheight(txt, width=None, **kwargs)¶
Returns the height of a string of text according to the current font settings.
- Parameters
txt – string to measure
width – width of a line of text in a block
- lineheight(height=None)¶
Set text lineheight.
- Parameters
height – line height.
- align(align='left')¶
Set text alignment
- Parameters
align – Text alignment (LEFT, CENTER, RIGHT)
- fontoptions(hintstyle=None, hintmetrics=None, subpixelorder=None, antialias=None)¶
Set font rendering options.
- Parameters
hintstyle – Hinting style (NONE, SLIGHT, MEDIUM, FULL)
hintmetrics – Quantize font metrics (ON, OFF)
antialias – Antialiasing type (NONE, GRAY, SUBPIXEL, FAST, GOOD, BEST)
subpixelorder – Order of pixels when antialiasing in SUBPIXEL mode (RGB, BGR, VRGB, VBGR)
- fontnames()¶
- autotext(sourceFile)¶
- property canvas¶
Not entirely sure compatible the Shoebot ‘canvas’ is with Nodebox but there you go. :return:
- angle(x0, y0, x1, y1)¶
- distance(x0, y0, x1, y1)¶
- coordinates(x0, y0, distance, angle)¶
- property FRAME¶
- MOUSEX = -1¶
The x-value of the mouse cursor coordinates.
- MOUSEY = -1¶
The y-value of the mouse cursor coordinates.
- choice(seq)¶
Choose a random element from a non-empty sequence.
- cm = 28.3465¶
- color(*args)¶
- Parameters
args – color in a supported format.
- Returns
Color object containing the color.
- files(path='*')¶
Returns a list of files.
Use wildcards to specify which files to pick, e.g.
f = files('*.gif')
.- Parameters
path – wildcard to use in file list
- Returns
list of file names
- finish()¶
- grid(cols, rows, colSize=1, rowSize=1, shuffled=False)¶
Returns an iterator that contains coordinate tuples.
This command can be used to quickly create grid-like structures. A common usage pattern is:
for x, y in grid(10,10,12,12): rect(x,y, 10,10)
- inch = 72¶
- key = '-'¶
- keycode = 0¶
- keydown = False¶
- mm = 2.8346¶
- mousedown = False¶
Is True if the mouse button is pressed.
- random(v1=None, v2=None)¶
- run(inputcode, max_iterations=None, run_forever=False, frame_limiter=False, verbose=False)¶
- show(format='png', as_data=False)¶
Returns an Image object of the current surface. Used for displaying output in Jupyter notebooks. Adapted from the cairo-jupyter project.
- size(w=None, h=None)¶
Set the canvas size
Only the first call will actually be effective.
- Parameters
w – Width
h – height
- snapshot(target=None, defer=None, autonumber=False)¶
Save the contents of current surface into a file or cairo surface/context.
- Parameters
filename – Can be a filename or a Cairo surface.
defer – When to snapshot, if set to True waits until the frame has finished rendering.
autonumber – If true then a number will be appended to the filename.
- speed(framerate=None)¶
Set animation framerate.
- Parameters
framerate – Frames per second to run bot.
- Returns
Current framerate of animation.
- var(name, type, default=None, min=0, max=255, value=None, step=None)¶
- ximport(libName)¶
Import Nodebox libraries.
The libraries get _ctx, which provides them with the nodebox API.
- Parameters
libName – Library name to import
Nodebox compatibility¶
Shoebot was originally developed as a rewrite of Nodebox, attempting to follow its behaviour as close as possible. However, the developers eventually wanted some functionality that was not in Nodebox, and there are many aspects of Nodebox that, for one reason or other, were not ported over.
Now that the original Nodebox isn’t being developed further, we have decided to go on and keep implementing original features whenever appropriate.
In this page, you’ll find the features and behavior that differs between Nodebox and Shoebot.
If you find any difference that isn’t documented here, please file an issue.
Additional Shoebot features¶
These are features that were created in Shoebot and are not available in Nodebox, or where Shoebot behavior is distinct.
New functions¶
Drawing
rectmode()
andellipsemode()
are inspired by Processing and cater to the preferences of users who expect alternative ways to draw primitives. These are also useful for specific situations where coordinate calculation would be otherwise necessary.arc()
is provided by Cairo, so we support it as well.
Others
snapshot()
is a simple way to output to an image in the middle of the script execution.run()
can execute another script in the current context. This is mostly useful when using Shoebot as a Python module inside another application.
Bundled libraries¶
Nodebox provides a set of external libraries that can be downloaded and added to a project. Shoebot comes with ported versions of those libraries already included and available.
New libraries¶
Additional external libraries were developed for Shoebot:
Unsupported Nodebox features¶
These are the Nodebox bits that aren’t available in Shoebot.
CMYK color¶
Nodebox was implemented using Mac OS X’s Cocoa toolkit, which supports CMYK color. Shoebot runs on the Cairo graphics backend, which does not. Nodebox commands that deal with CMYK color are therefore unsupported:
outputmode() is not available.
colormode() is implemented, but
CMYK
is not an accepted argument – onlyRGB
orHSB
.
Path operations¶
Operations between bézier paths are not supported yet. These are:
path.union()
path.intersect()
path.difference()
Fitting a path using path.fit()
isn’t supported either.
Animation¶
While animations work well in a window, Shoebot does not support exporting them to GIF or video formats.
Unported libraries¶
Knowledge: WordNet, Keywords, Linguistics
Bitmap: Core Image, iSight, Quicktime
Design: Grid
Other: Flowerewolf, twyg
Contributing¶
Non-development tasks¶
Help improve our documentation¶
We’re missing a few details and we’d definitely welcome some help here!
Port Nodebox library pages¶
Most of the pages in the libraries section are stubs. While the original Nodebox documentation is good enough reference, it’s becoming important to port those docs over to the Shoebot manual because most of the images are missing on the Nodebox site. (The PlotDevice manual thankfully keeps its own adaptation.)
It’s not hard to adapt the Nodebox docs to the Shoebot manual: docs are written in the ReStructured Text format (a kind of souped-up Markdown), and you can take a look at the source of the colors library page for an example of what Sphinx files look like. This cheat sheet explains the basics. From there, you can help complete the stub pages, which would be just excellent.
Report errors or missing parts¶
Pointing out parts that are missing or wrong is a great way to help. If you spot
something, file an issue with the documentation
label on the issue tracker
to help us on this, or just pop over on the Matrix channel.
Development tasks¶
Make new examples or port existing ones¶
We’re always eager to welcome new examples that explain a concept or show off an interesting technique.
You can either contribute your own examples, or help port existing scripts:
the nodebox-pyobjc examples, which are more current than those in the old Nodebox 1 repository
the scripts in the Nodebox gallery
They should work mostly without modifications – we need help testing them. Try them out and post any issues you find on our issue tracker in case you hit a wall.
Be sure to also check the brief guidelines in Coding style for examples so that your efforts can be included in Shoebot.
Help port libraries¶
We’re missing a few Nodebox libraries; can you help us port them to Shoebot?
See the full list of Unported libraries.
Incidentally, we’re also missing documentation to explain how to port Nodebox libraries. If you’re interested but stuck, file an issue and we’ll help you.
Add support for another operating system¶
We support a few operating systems, but we’d like to expand the list.
To add support for another OS, you just need to find out its package names for the libraries that Shoebot depends on:
Python 3
Pycairo
PyGObject3
Pango
Pillow
GTK3 (for the windowed canvas)
GTKSourceView3 (for the IDE)
The community for your operating system may be able to offer help here. The install_dependencies.sh script might help too.
Look for ‘Help Out’ issues¶
The issues tagged ‘Help Out’ don’t need a deep knowledge of Shoebot internals, and there’s a good variety of tasks to be done.
Make text editor plugins¶
While our simple editor is around, power-users will be using their favourite text editor to hack on Shoebot scripts. Having plugins for any popular text editor would be a fantastic addition.
Integrate Shoebot with other software¶
Shoebot can be a great tool to complement other software, be it for
SVG, PDF or bitmap generation
simple visualizations
interact in real-time with the socket server
If you see a use case where Shoebot could be helpful, we’ll do our best to support you in implementing it.
Tips for Developers¶
Coding style for the Shoebot core code¶
We’re not picky here, other than following PEP8 style guidelines. We use the Black syntax checker in our code editors to keep us strict, and recommend it.
Coding style for examples¶
When creating examples for including in Shoebot, we try to adhere to a set of writing guidelines to make it easy for newcomers to understand what’s going on.
Avoid one-letter variables (other than
x
andy
), and avoid two-letter names as well (things likedx
can be expanded todeltax
). It will look less compact, but really helps understanding what’s going on.Start the example with a docstring specifying the title of the example, author info and some details about the script and its workings. If you want to format this text, use Markdown.
Use Flake8 or similar linter plugin to find necessary style fixes.
Comments in English.
Variables and functions are in
lowercase
andunderscored_lowercase
, class names are inCamelCase
.
Making a release¶
This is our checklist to be sure we don’t miss any detail when we put out a release.
update the version number in these files:
Makefile
VERSION
setup.py
doc/source/conf.py
shoebot/ide/ide.py
update the changelogs
CHANGELOG
debian/changelog
tag the release commit
publish release on GitHub
push to PyPI
register on PyPI and place your credentials in
~/.pypirc
install Twine
make a source build with
python setup.py sdist
make a test upload to TestPyPI with
twine upload --repository-url https://test.pypi.org/legacy/ dist/shoebot-1.3.tar.gz
if all is good, upload to PyPI with
twine upload dist/shoebot-1.3.tar.gz
be sure to change the version numbers in the previous commands according to the current Shoebot version
Building Debian packages¶
There are some dependencies to look out for:
sudo apt-get install rename dh-python cdbs
Be sure to go through this checklist:
update the debian/changelog file
Then, generate the Debian packages with the make builddeb command.