๐Ÿฅ Make a Cartoon Walk
Cut Up a Picture & Bring It to Life

You make one character picture and one background โ€” any way you like. Then, with the free tools already on your Mac (Preview and the Terminal), a little Python, and Blender, you'll cut your character into a paper-doll puppet and make it walk across the screen.

For makers ~12+ ยท a Mac + Blender + your own pictures

โ–ถ Where this trick can take you: two royal cats walking and talking across a palace. You'll build the simple version โ€” one character, two legs, one walk.

๐ŸŽฏ Your Mission

By the end of this page you'll have walk.mp4 โ€” your own character strolling across your own scene. The whole project is just four little pictures and one script:

background.png (the scene) + main.png (the body) + near_leg.png + far_leg.png (the two legs) โ†’ fed to walk.py โ†’ out pops a movie. ๐ŸŽฌ

๐Ÿงฉ The Big Idea: It's a Puppet Show

A cartoon looks alive, but it's a trick. Animators don't redraw the whole character for every tiny movement. They make a cut-out puppet โ€” like a paper doll โ€” and move the pieces a little at a time.

Picture a paper bird held together with split-pins at the hips: you can swing its legs without redrawing it. That's the entire recipe:

1. A scene(background) + 2. Acharacter โœ‚ 3. Cut into3 parts ๐Ÿ“Œ 4. Pin &swing them โ–ถ VIDEO!

Here's our puppet โ€” a bird with three parts: a body and two legs, pinned at the hips. Every step below builds exactly this:

hips โ€” the legs swing here

The blue dots are the pivots (the split-pins). A leg swings around its pivot โ€” at the hip, never the middle.

๐Ÿงฐ What You Need

๐Ÿ’ป A Mac โ€” you'll use two things already on it: Preview (for cutting pictures) and the Terminal (for running things).
๐ŸŸง Blender โ€” a free, professional animation program from blender.org. The next card shows exactly how to install it.
๐Ÿ Python โ€” only if you choose the code way of cutting pictures. Macs come with python3 in the Terminal.
๐Ÿ–ผ๏ธ Two pictures YOU make โ€” a character (seen from the side) and a background. Draw them on paper and photograph them, paint them in an app, or use an AI image tool โ€” your choice!

๐ŸŸง Install Blender on Your Mac

Before the fun starts, Blender needs to live on your Mac โ€” and your Terminal needs to understand the word blender. You do this once and you're set forever. (A grown-up can help with the first launch.)

The click-and-drag way (easiest)

  1. Open a web browser and go to blender.org/download.
  2. Click the big download button. Pick Apple Silicon if your Mac is an M1/M2/M3/M4 (most newer Macs) or Intel for an older one. Not sure? The Apple menu โ†’ About This Mac tells you which chip you have.
  3. Open the file it downloads (a .dmg, waiting in your Downloads folder).
  4. A little window pops up โ€” drag the Blender icon onto the Applications folder shown next to it. That installs it.
  5. The very first time you open Blender, hold the Control key, click the Blender icon, choose Open, then Open again. This tells your Mac you trust it โ€” you only do this once.

Make the word blender work in the Terminal

At the end of this guide you'll run Blender by typing blender in the Terminal. But on a Mac the real program hides inside the app, here:

/Applications/Blender.app/Contents/MacOS/Blender

So teach your Terminal a short-cut (called an alias). Open Terminal (find it with the ๐Ÿ” Spotlight search) and paste these two lines, pressing Return after each:

echo 'alias blender="/Applications/Blender.app/Contents/MacOS/Blender"' >> ~/.zshrc source ~/.zshrc

The first line writes the short-cut into your Terminal's settings file; the second loads it. Now test it:

blender --version
It prints something like Blender 5.0. Now every command in this guide will work.
Heads-up: Blender is a big download (several hundred MB), so it can take a few minutes on slow Wi-Fi. Let it finish before the first launch.

๐Ÿ“š The Recipe โ€” six small steps

1 Make Your Two Pictures

This part is all yours โ€” draw, paint, photograph, or generate. You need:

Make a new folder for the project (call it walk) and put both pictures inside.

Drawing on paper? Great! Use a dark outline, photograph it straight-on in good light, and AirDrop or email it to your Mac. The plainer the paper, the easier the next step.
A folder called walk with two files inside: background.png and character.png.

2 Make the Background See-Through

Your character needs to sit on top of the scene โ€” so the plain background around it must become invisible. Pictures store this as a fourth number per dot called alpha: 255 = solid, 0 = see-through.

๐Ÿ–ฑ๏ธ The Preview way (no code)

  1. Open character.png in Preview (double-click it).
  2. Click the Markup button (the pencil-tip โœ๏ธ in a circle, top right).
  3. Click the Instant Alpha tool (the magic wand ๐Ÿช„).
  4. Click on the plain background and drag a little โ€” it glows red. The further you drag, the more it selects.
  5. Press Delete. If Preview asks to convert the image to PNG, click Convert. The background vanishes into grey checkers โ€” that means see-through!
  6. Repeat for any leftover patches (like between the legs), then press โŒ˜S to save.

๐Ÿ The Python way (if you like code)

This does the same thing for a plain white background: it looks at every dot, and any dot that's nearly white gets its alpha set to 0. In the Terminal, run pip3 install pillow once, then save this as cutout.py in your folder and run python3 cutout.py:

from PIL import Image

img = Image.open("character.png").convert("RGBA")   # RGBA = Red, Green, Blue + Alpha
dots = img.load()
w, h = img.size

for y in range(h):
    for x in range(w):
        r, g, b, a = dots[x, y]
        if r > 240 and g > 240 and b > 240:         # nearly white? that's the paper
            dots[x, y] = (r, g, b, 0)               # alpha 0 = invisible

img.save("character.png")
Gotcha: the Python way erases white everywhere โ€” including white eyes or a white tummy on your character! If your character has white bits, use Preview's Instant Alpha instead โ€” you control exactly what disappears.
Open character.png โ€” your character sits on grey checkers (or nothing), with no colored box around it.

3 Cut It Into Three Parts โœ‚๏ธ

Now the puppet magic. A character in one piece can't move its legs โ€” so you'll turn one picture into three: a body and two legs. Each part is just a rectangle cut from the original:

main.png โ€” body, down to just BELOW the hips near_leg.png far_leg.png

Three rectangles. The body box reaches a little below the hips, and each leg box starts exactly at the hip โ€” so the boxes overlap a bit.

Two rules make or break the puppet:

๐Ÿ–ฑ๏ธ The Preview way (no code)

  1. In Finder, click character.png and press โŒ˜D three times. Rename the copies main.png, near_leg.png, far_leg.png.
  2. Open main.png in Preview. Drag a rectangle around the body โ€” from the top of the head down to just below the hips. Press โŒ˜K (Tools โ†’ Crop). Save with โŒ˜S.
  3. Open near_leg.png. Drag a rectangle around just the front leg โ€” top edge exactly at the hip, down past the foot. โŒ˜K, โŒ˜S.
  4. Open far_leg.png. Same thing for the back leg. โŒ˜K, โŒ˜S.

๐Ÿ The Python way (if you like code)

Crops are rectangles too: crop((left, top, right, bottom)) โ€” four numbers, all counted in pixels from the picture's top-left corner. So first you need to measure where the hips and legs are in your picture.

๐Ÿ“ Find your numbers โ€” Preview is a pixel ruler

Open character.png in Preview and start dragging a selection at the picture's very top-left corner. The little grey badge by the cursor counts the pixels โ€” so wherever you stop, the badge is reading that spot's (left, top) position. Drag from the corner to the front leg's hip and the badge says something like 240 ร— 575 โ€” you just measured: left = 240, top = 575. Measure each corner you need this way and jot the numbers down. (Press Esc to drop a selection and measure again.)

Another ruler, for coders: run pip3 install matplotlib once, then
python3 -c "import matplotlib.pyplot as plt; plt.imshow(plt.imread('character.png')); plt.show()"
Your picture opens in a window, and as you move the mouse the corner shows x=โ€ฆ y=โ€ฆ โ€” live pixel coordinates.

Now put your numbers into this script. Save it as parts.py, run python3 parts.py:

from PIL import Image

bird = Image.open("character.png")
w, h = bird.size                  # the whole picture: w wide, h tall, in pixels

bird.crop((  0,   0,   w, 620)).save("main.png")      # body: full width, top -> just BELOW the hips
bird.crop((240, 575, 400,   h)).save("near_leg.png")  # front leg: hip -> foot
bird.crop((415, 575, 575,   h)).save("far_leg.png")   # back leg: hip -> foot

Those numbers fit our bird โ€” yours will be different. But notice the overlap: the body reaches down to 620 while the legs start up at 575 โ€” that 45-pixel band is where the body hides the seam.

Don't measure too hard. Close is good enough: guess, run parts.py, open the three PNGs, fix the worst number, run again. Two or three rounds and it's perfect โ€” that's how real animators work too.
Your folder now has four pictures: background.png, main.png, near_leg.png, far_leg.png โ€” and each leg file shows only a leg, starting at its hip.

4 Pin the Legs to the Body ๐Ÿ“Œ

In a paper puppet you'd push a split-pin through the hip. In Blender, the pin is an invisible point called an Empty: the leg is attached ("parented") to it, so when the pin turns, the whole leg swings from the hip.

The script you'll run (walk.py โ€” download in step 6) does this for you, automatically:

Nothing to do in this step โ€” except understand the trick: pin at the hip = swings like a leg. Pin in the middle = spins like a propeller. ๐Ÿš

5 Make the Legs Go Back and Forth

A walk is just legs taking turns. Each frame of the video, the script rotates the two hip-pins by a few degrees โ€” in opposite directions:

frame 0frame 12frame 25 โ†’โ†’

One leg swings forward while the other swings back โ€” exactly like your own legs.

Inside walk.py it's one little formula, run once per frame:

angle = SWING_DEG * sin(...)        # a smooth back-and-forth wave
near_hip.rotate( angle)             # near leg swings one way...
far_hip.rotate(-angle)              # ...far leg swings the OPPOSITE way

sin(...) makes a smooth wave that glides from +1 to โˆ’1 and back โ€” so the legs speed up and slow down gently instead of snapping. The minus sign on the far leg is the whole secret of walking.

Nothing to run yet โ€” but now you can read the two knobs that shape the walk: SWING_DEG (how far the legs kick) and STEPS_SEC (how fast they step).

6 Walk Across the Screen โ€” and Render! ๐ŸŽฌ

Swinging legs alone is walking on the spot. The last trick: every frame, the script also slides the whole puppet (body, pins, legs โ€” everything) a little to the left:

๐Ÿฅ ๐Ÿฅ ๐Ÿฅ start: right edge โ†’ end: left edge

Legs swinging + the whole puppet sliding = a real walk across the scene.

Run it

  1. โฌ‡ Download walk.py and drop it in your walk folder, next to the four pictures.
  2. Open the Terminal and go to your folder (type cd with a space, drag the folder from Finder onto the Terminal window, press Return).
  3. Run Blender with the script โ€” -b means "no window", -P means "run this Python file":
blender -b -P walk.py -- .

Blender draws all 100 frames (a minute or two), then drops walk.mp4 into the folder. Double-click it.

๐ŸŽ‰ You did it! Your character walks all the way across your scene, legs stepping the whole way. That's a real animation that YOU built from two pictures.

Tweak the knobs & go again

Open walk.py in any text editor โ€” the top has friendly knobs. Change one, run the command again, watch what happens:

KnobWhat it doesTry
SECONDShow long the walk lasts8 for a slow stroll
SWING_DEGhow far the legs kick35 for a silly march
STEPS_SECsteps per second4 for scurrying
TRAVELhow far across it walks0.45 edge to edge

๐Ÿ“œ walk.py โ€” the Whole Script

Here it is โ€” all of it. Every line has a comment saying what it does, and it's the exact same recipe you just learned: flat pictures โ†’ pins at the hips โ†’ opposite swings โ†’ slide across โ†’ render. Grab it either way:

โฌ‡ Download walk.py
"""walk.py - animate a 4-image puppet walking right->left, render to an MP4.

Run it with Blender, no window needed:
    blender -b -P walk.py -- /path/to/folder

The folder must contain four pictures:
    background.png   main.png   near_leg.png   far_leg.png
(the character parts should be PNGs with a see-through background)
"""
import bpy, sys, os, math

# ---- knobs you can tweak ----------------------------------------------------
FPS        = 25      # frames per second
SECONDS    = 4       # how long the walk lasts
SWING_DEG  = 22      # how far the legs swing, in degrees
STEPS_SEC  = 2       # how many steps per second
WORLD_W    = 10.0    # width of the scene, in Blender units
RES_X, RES_Y = 1280, 720
CHAR_FRAC  = 0.55    # the character's height as a fraction of the screen height
TRAVEL     = 0.40    # how far across to walk (fraction of the screen each side)
# ----------------------------------------------------------------------------

# read the folder path that comes after the "--" on the command line
argv   = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
folder = argv[0] if argv else "."
FRAMES = FPS * SECONDS
FRAME_W = WORLD_W                       # the camera frame is WORLD_W units wide
FRAME_H = WORLD_W * RES_Y / RES_X       # ...and this tall (keeps the 16:9 shape)

# start from a totally empty scene
bpy.ops.wm.read_factory_settings(use_empty=True)
scene = bpy.context.scene
scene.render.fps = FPS
scene.frame_start, scene.frame_end = 0, FRAMES

def flat_material(img):
    """A material that shows a picture flat (no lighting) and respects its alpha."""
    mat = bpy.data.materials.new(img.name)
    mat.use_nodes = True
    nt = mat.node_tree
    nt.nodes.clear()
    tex   = nt.nodes.new("ShaderNodeTexImage");      tex.image = img
    emit  = nt.nodes.new("ShaderNodeEmission")
    clear = nt.nodes.new("ShaderNodeBsdfTransparent")
    mix   = nt.nodes.new("ShaderNodeMixShader")
    out   = nt.nodes.new("ShaderNodeOutputMaterial")
    nt.links.new(tex.outputs["Color"], emit.inputs["Color"])  # picture colour -> glows flat
    nt.links.new(tex.outputs["Alpha"], mix.inputs["Fac"])     # picture alpha -> see-through-ness
    nt.links.new(clear.outputs["BSDF"], mix.inputs[1])        # where alpha=0 -> transparent
    nt.links.new(emit.outputs["Emission"], mix.inputs[2])     # where alpha=1 -> the picture
    nt.links.new(mix.outputs["Shader"], out.inputs["Surface"])
    for attr, val in (("blend_method", "BLEND"), ("surface_render_method", "BLENDED")):
        try: setattr(mat, attr, val)        # the name for "let alpha show" differs by Blender version
        except Exception: pass
    return mat

def plane(name, filename, z):
    """Make a flat plane that displays one picture. Returns the plane + its pixel size."""
    img = bpy.data.images.load(os.path.join(folder, filename))
    w, h = img.size
    bpy.ops.mesh.primitive_plane_add(size=2)    # a 2x2 plane (so half-width = 1 before scaling)
    pl = bpy.context.active_object
    pl.name = name
    pl.rotation_euler = (0, 0, 0)
    pl.location.z = z                           # bigger z = nearer the camera = drawn in front
    pl.data.materials.append(flat_material(img))
    return pl, w, h

def fit(pl, w_px, h_px, units_per_px):
    """Scale a plane so each pixel is the same size everywhere (keeps shapes correct)."""
    pl.scale = (w_px * units_per_px / 2, h_px * units_per_px / 2, 1)

# --- background: scale it to COVER the whole frame -----------------------------
bg, bw, bh = plane("background", "background.png", 0.0)
cover = max(FRAME_W / bw, FRAME_H / bh)         # the bigger ratio guarantees full coverage
fit(bg, bw, bh, cover)

# --- character parts: all share ONE pixel scale so body + legs stay consistent -
body, mw, mh = plane("main",     "main.png",     0.3)
near, nw, nh = plane("near_leg", "near_leg.png", 0.2)
far,  fw, fh = plane("far_leg",  "far_leg.png",  0.1)
upx = (CHAR_FRAC * FRAME_H) / mh                # pick a scale so the body is CHAR_FRAC tall
for pl, (w, h) in ((body, (mw, mh)), (near, (nw, nh)), (far, (fw, fh))):
    fit(pl, w, h, upx)

# vertical layout: feet on the ground, hips above the feet, body stacked on the hips
FOOT_Y = -FRAME_H / 2 + 0.20            # the feet rest just above the bottom edge
HIP_Y  = FOOT_Y + nh * upx              # the hips sit one leg-length up from the feet

# place the body: centred left-right, resting on the hips (overlapping them a touch)
body.location = (0, HIP_Y - 0.15 + mh * upx / 2, 0.3)
bpy.context.view_layer.update()         # make Blender recompute positions before we read them

def hip(leg, w_px, h_px, x_off, z):
    """Give a leg a pivot (an Empty) at its TOP edge, so it swings from the hip."""
    leg.location = (x_off, FOOT_Y + h_px * upx / 2, z)        # stand the leg on the ground
    pin = bpy.data.objects.new(leg.name + "_hip", None)       # an Empty = an invisible pin
    scene.collection.objects.link(pin)
    pin.location = (x_off, FOOT_Y + h_px * upx, z)            # pin at the TOP of the leg = the hip
    bpy.context.view_layer.update()
    leg.parent = pin                                          # the leg now hangs from the pin...
    leg.matrix_parent_inverse = pin.matrix_world.inverted()   # ...without jumping when parented
    return pin

near_hip = hip(near, nw, nh, -0.12 * mw * upx, 0.2)   # front leg, slightly forward
far_hip  = hip(far,  fw, fh, +0.12 * mw * upx, 0.1)   # back leg, slightly back

# one ROOT pin that carries the whole character, so we can slide it across
root = bpy.data.objects.new("root", None)         # sits at the origin (0,0,0)
scene.collection.objects.link(root)
bpy.context.view_layer.update()
for ob in (body, near_hip, far_hip):              # body + both hips ride on the root
    ob.parent = root
    ob.matrix_parent_inverse = root.matrix_world.inverted()

# --- the animation: keyframe every frame -------------------------------------
for f in range(FRAMES + 1):
    t = f / FPS
    angle = math.radians(SWING_DEG) * math.sin(2 * math.pi * STEPS_SEC * t)  # smooth back-and-forth
    near_hip.rotation_euler[2] =  angle          # legs swing in OPPOSITE directions...
    far_hip.rotation_euler[2]  = -angle          # ...so it looks like real walking
    near_hip.keyframe_insert("rotation_euler", index=2, frame=f)
    far_hip.keyframe_insert("rotation_euler", index=2, frame=f)
    root.location.x = FRAME_W * TRAVEL - 2 * FRAME_W * TRAVEL * (f / FRAMES)   # slide right -> left
    root.keyframe_insert("location", index=0, frame=f)

# --- an orthographic camera, looking straight on for a flat 2D look ----------
cam_data = bpy.data.cameras.new("cam")
cam_data.type = 'ORTHO'
cam_data.ortho_scale = WORLD_W
cam = bpy.data.objects.new("cam", cam_data)
scene.collection.objects.link(cam)
cam.location = (0, 0, 10)                         # above the planes, looking down the -Z axis
scene.camera = cam

# --- render straight to an MP4 -----------------------------------------------
scene.render.resolution_x, scene.render.resolution_y = RES_X, RES_Y
try: scene.render.image_settings.media_type = 'VIDEO'   # Blender 5.x: switch to video output first
except Exception: pass                                  # (older Blender has no media_type)
scene.render.image_settings.file_format = 'FFMPEG'
scene.render.ffmpeg.format = 'MPEG4'
scene.render.ffmpeg.codec  = 'H264'
scene.render.filepath = os.path.join(folder, "walk.mp4")
bpy.ops.render.render(animation=True)
print("Saved", scene.render.filepath)
If you copied: open TextEdit, press โŒ˜โ‡งT to switch to plain text, paste, and save as walk.py in your walk folder (not .txt, not .rtf!). Downloading skips all that โ€” the file lands in Downloads, ready to drag into your folder.

๐Ÿฉน If Something Looks Wrong

๐Ÿถ Does It Work for Any Character? Yes!

Same recipe, different puppet: a flat Peppa-style dog crossing a sunny field โ€” a scene, a cut-out character, a body and two legs, pinned at the hips, swinging opposite, sliding across.

โ–ถ Built with the exact same six steps. Now try it with your character!

๐ŸŽค Now Make Your Cartoon TALK

Your character can walk โ€” give it a voice next. The companion guide shows four ways to make a cartoon talk: AI lip-sync with Wav2Lip, sharpening the face, the on-style mouth-transplant trick, and โ€” for shapes-and-lines characters like a Peppa-style piglet โ€” drawing a mouth that opens to the sound.

๐Ÿ› ๏ธ Your Toolkit, One More Time

๐Ÿ–ผ๏ธ Preview โ€” Instant Alpha makes backgrounds see-through; select + โŒ˜K cuts the parts.
๐Ÿ Python (PIL) โ€” the code way to do the same cutting, with numbers instead of dragging.
๐ŸŸง Blender โ€” pins the puppet, swings the legs, renders walk.mp4.
โŒจ๏ธ Terminal โ€” one line, blender -b -P walk.py -- ., runs the whole show.
Ask a grown-up before installing software, using AI tools, or uploading anything online.

๐Ÿš€ More Cartoon Magic

Liked making things move? There are two more ways to build cartoons on this site โ€” one where AI draws and animates for you in the cloud, and one where the AI art studio runs on your own Mac: