Skip to main content

Create videos that simulate typing code with a smooth camera following the cursor

Project description

CVRer logo

CVRer

A Python library for creating videos that simulate typing code, with a smooth camera that follows the cursor.

Note: This is a reimplementation of the original CodeVideoRenderer and PypiProject by ZhuChongjing. The original uses manim as its rendering engine, which requires Microsoft C++ Build Tools to install on Windows. This version replaces manim entirely with Pillow + moviepy, so it works out of the box on any Python 3.8+ environment without any C++ compiler.


Differences from the original

Feature Original This version
Rendering engine manim Pillow + moviepy
Requires C++ Build Tools Yes No
Camera follow Smooth X+Y via manim scene Smooth X+Y via lerp per frame
Cursor blink Yes Yes
Glow effect moviepy moviepy (same)
reverse_typing No Yes
Custom font Yes (fonts/ folder) Yes (fonts/ folder)
Line numbers Yes Yes
center layout No Yes

Known limitations compared to the original:

  • No OpenGL renderer option (Cairo only in original, here it's Pillow)
  • No random typing interval variation per character (use interval_range=(0.08, 0.15) for variation — min/max are applied uniformly, not per-character randomly)
  • Camera follows cursor but does not do the subtle vertical oscillation the original does while typing a long line
  • formatter_style only supports pygments built-in styles — custom VS Code themes require passing color dicts manually

Requirements

Pillow>=9.0
pygments>=2.10
moviepy==1.0.3
rich>=13.0
proglog

Install:

pip install Pillow pygments "moviepy==1.0.3" rich proglog

Quick start

import sys
sys.path.insert(0, '/path/to/folder')  # folder containing CVRer/
from CVRer import CameraFollowCursorCV

cv = CameraFollowCursorCV(
    code=('string', """
def hello():
    name = "World"
    print(f"Hello, {name}!")
    return name

result = hello()
print(result)
"""),
    language='python',
    formatter_style='github-dark',
)
cv.render()

Output: CameraFollowCursorCV.mp4 in the current directory.


CameraFollowCursorCV

Constructor parameters

code — required

Source code to animate. Two formats:

# From a string
code=('string', 'print("hello")')

# From a file
code=('file', 'path/to/script.py')

The file must be UTF-8 encoded. Characters \r, \v, \f are not allowed.


language — required

Pygments language alias. Examples:

language='python'
language='html'
language='javascript'
language='typescript'
language='cpp'
language='rust'
language='go'
language='sql'
language='bash'
language='json'
language='yaml'

Full list: run from pygments.lexers import get_all_lexers; print(list(get_all_lexers())) or see CVRer/typing.py.


formatter_style — default: "github-dark"

Syntax highlight color theme. Must be a valid pygments style name.

formatter_style='github-dark'    # dark, GitHub-style
formatter_style='monokai'        # classic dark
formatter_style='dracula'        # purple dark
formatter_style='one-dark'       # Atom One Dark
formatter_style='nord'           # Nordic blue-grey
formatter_style='gruvbox-dark'   # warm retro dark
formatter_style='solarized-dark' # Solarized
formatter_style='vs'             # light, Visual Studio-like
formatter_style='xcode'          # light, Xcode-like

Full list:

from pygments.styles import get_all_styles
print(list(get_all_styles()))

font_size — default: 26

Font size in pixels. The library first looks for a .ttf file in CVRer/fonts/, then falls back to system fonts (Consolas, Courier New, Lucida Console).

font_size=20   # smaller, more code fits on screen
font_size=26   # default
font_size=32   # larger, easier to read

To use a custom font — place any .ttf file into CVRer/fonts/. It will be picked up automatically.


line_spacing — default: 1.5

Line height multiplier relative to the font's character height.

line_spacing=1.2   # compact
line_spacing=1.5   # default, comfortable
line_spacing=2.0   # spacious

interval_range — default: (0.15, 0.15)

Typing speed as (min_seconds, max_seconds) per character. This also determines the video FPS: fps = round(1 / min_seconds).

interval_range=(0.15, 0.15)   # 0.15s per char, ~7 fps — slow, readable
interval_range=(0.08, 0.08)   # 0.08s per char, ~12 fps — medium
interval_range=(0.05, 0.05)   # 0.05s per char, ~20 fps — fast
interval_range=(0.03, 0.03)   # 0.03s per char, ~33 fps — very fast

Currently both values must be equal. Per-character random variation is not yet implemented.


camera_scale — default: 1.0

Output video resolution scale.

camera_scale=1.0   # 1920x1080 (Full HD)
camera_scale=0.5   # 960x540
camera_scale=0.75  # 1440x810

camera_zoom — default: 2.0

How close the camera is to the code. Higher = more zoomed in, fewer characters visible at once.

camera_zoom=1.0   # wide view, lots of code visible
camera_zoom=1.5   # slightly zoomed
camera_zoom=2.0   # default, comfortable close-up
camera_zoom=3.0   # very close, ~20 chars per line visible

Internally this crops a smaller region of the canvas and upscales it to the output resolution.


camera_smooth — default: 0.25

Camera movement smoothing coefficient per frame. Range: 0.0 to 1.0.

camera_smooth=0.05   # very slow, cinematic drift
camera_smooth=0.15   # smooth, natural
camera_smooth=0.25   # default
camera_smooth=0.5    # snappy, follows cursor closely
camera_smooth=1.0    # instant, no smoothing

end_pause — default: 2.0

Seconds the video holds after all characters are typed. During this time the camera continues smoothly settling and the cursor keeps blinking.

end_pause=0.5   # quick cut
end_pause=2.0   # default
end_pause=5.0   # long hold

center — default: True

Whether to center the code block within the viewport. When True, the code appears in the middle of the frame rather than top-left.

center=True    # code centered in frame
center=False   # code starts at top-left

cursor_blink_rate — default: 0.5

Cursor blink period in seconds. The cursor is visible for half the period, hidden for the other half. Set to 0 to disable blinking.

cursor_blink_rate=0.5   # default, blinks every 0.5s
cursor_blink_rate=1.0   # slow blink
cursor_blink_rate=0.3   # fast blink
cursor_blink_rate=0     # no blinking, always visible

video_name — default: "CameraFollowCursorCV"

Output file name without the .mp4 extension. Can be an absolute path.

video_name='my_video'                          # saves as my_video.mp4 in cwd
video_name='C:/Users/User/Desktop/output'      # absolute path
video_name='renders/python_tutorial'           # relative subfolder

line_numbers — default: True

Show line numbers on the left side.

line_numbers=True    # show line numbers
line_numbers=False   # hide line numbers

glow — default: True

Apply a glow/bloom post-processing effect after rendering. Uses moviepy to process each frame with Gaussian blur + brightness boost. Adds noticeable processing time.

glow=True    # apply glow (slower, looks better)
glow=False   # skip glow (faster)

render() method

cv.render(
    output=True,           # print progress to console
    reverse_typing=False,  # erase code after end_pause
)

output — default: True

Print rendering progress, fps, resolution info and timing to the console.

reverse_typing — default: False

After the end_pause, erase all typed characters one by one in reverse order until the screen is completely empty. The cursor keeps blinking during erasure.

cv.render(reverse_typing=True)

Full example

import sys
sys.path.insert(0, 'C:/Users/User/Desktop/MPCode')
from CVRer import CameraFollowCursorCV

cv = CameraFollowCursorCV(
    code=('string', """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- Code -->
</body>
</html>
"""),
    language='html',
    formatter_style='github-dark',
    font_size=26,
    line_spacing=1.5,
    interval_range=(0.08, 0.08),
    camera_scale=1.0,
    camera_zoom=2.0,
    camera_smooth=0.15,
    end_pause=3.0,
    center=True,
    cursor_blink_rate=0.5,
    video_name='C:/Users/User/Desktop/output',
    line_numbers=True,
    glow=True,
)
cv.render(output=True, reverse_typing=False)

Load from file

cv = CameraFollowCursorCV(
    code=('file', 'C:/path/to/folder/script.py'),
    language='python',
    formatter_style='monokai',
    video_name='C:/Users/User/Desktop/script_video',
)
cv.render()

Available highlight styles

from pygments.styles import get_all_styles
print(sorted(get_all_styles()))
# ['abap', 'algol', 'arduino', 'autumn', 'dracula', 'emacs',
#  'friendly', 'fruity', 'github-dark', 'gruvbox-dark', 'gruvbox-light',
#  'inkpot', 'material', 'monokai', 'native', 'nord', 'nord-darker',
#  'one-dark', 'paraiso-dark', 'solarized-dark', 'solarized-light',
#  'tango', 'vim', 'vs', 'xcode', 'zenburn', ...]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cvrer-0.1.0.tar.gz (7.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cvrer-0.1.0-py3-none-any.whl (7.3 MB view details)

Uploaded Python 3

File details

Details for the file cvrer-0.1.0.tar.gz.

File metadata

  • Download URL: cvrer-0.1.0.tar.gz
  • Upload date:
  • Size: 7.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.0

File hashes

Hashes for cvrer-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7266391e8f7abd74fec162e48ff0119d49b24c68b448dcada1befdad5d7c0dc9
MD5 404e0b064c7c1ed0890549431fe5dd8d
BLAKE2b-256 4f93d2a5070773346b1ecb3e1e4f78c2a9b4effa8a83b4df48a978af137f8c66

See more details on using hashes here.

File details

Details for the file cvrer-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cvrer-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.3 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.0

File hashes

Hashes for cvrer-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 82533638a5ac29ce81c17639a3d4479a1183cc2c6fd71ed4768326c6d8b3f5be
MD5 78e5f45713682283a9933b0c544042f5
BLAKE2b-256 4b0f2f99336c1a0937e7dcf88f38c4c8e110474b6ac17b5f04f73bebcfa4813f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page