Making videos for the web with Godot 4’s Movie Writer
Normally I use OBS for screen recording, but there are cases where it makes sense to use Godot’s built-in movie writer that was recently announced.
For example, if you have a slow PC or really demanding game, OBS will skip frames. It makes sense since OBS is just recording what’s on the screen in real time, while Godot has to do most of the work running the game. Recording with Godot directly will slow down the action at recording time, and you cannot hear any audio while that happens. But the video playback will end up being buttery smooth at 60 fps and include audio. It will also result in huge files. 321MB for 8 seconds, at 1280x800px (also Steam Deck’s native 16:10 resolution). Still, that’s not a video file I want to embed into any webpage or wherever as-is.
The best tool for converting video from the command line or programmatically is still ffmpeg. I’m running this on Windows in WSL2, on Ubuntu 22.04.
The best option for .webm seems to be the VP9 codec. Their docs provide some examples. I’ve tested a few parameters. For two-pass targeting an average bitrate, the target bitrate is specified with the -b:v switch:
# will take longer but smaller fileffmpeg-itilemap.avi-c:vlibvpx-vp9-b:v2M-pass1-an-fnull/dev/null&&\ffmpeg-itilemap.avi-c:vlibvpx-vp9-b:v2M-pass2-c:alibopustilemap-average.webm
This is what the output looks like:
gray@Battlestation:/mnt/c/Users/gray/godot/arpg$ffmpeg-itilemap.avi-c:vlibvpx-vp9-b:v2M-pass1-an-fnull/dev/null&&\ffmpeg-itilemap.avi-c:vlibvpx-vp9-b:v2M-pass2-c:alibopustilemap-average.webmffmpegversion4.4.2-0ubuntu0.22.04.1Copyright (c) 2000-2021 the FFmpeg developersbuiltwithgcc11 (Ubuntu 11.2.0-19ubuntu1)configuration:--prefix=/usr--extra-version=0ubuntu0.22.04.1--toolchain=hardened--libdir=/usr/lib/x86_64-linux-gnu--incdir=/usr/include/x86_64-linux-gnu--arch=amd64--enable-gpl--disable-stripping--enable-gnutls--enable-ladspa--enable-libaom--enable-libass--enable-libbluray--enable-libbs2b--enable-libcaca--enable-libcdio--enable-libcodec2--enable-libdav1d--enable-libflite--enable-libfontconfig--enable-libfreetype--enable-libfribidi--enable-libgme--enable-libgsm--enable-libjack--enable-libmp3lame--enable-libmysofa--enable-libopenjpeg--enable-libopenmpt--enable-libopus--enable-libpulse--enable-librabbitmq--enable-librubberband--enable-libshine--enable-libsnappy--enable-libsoxr--enable-libspeex--enable-libsrt--enable-libssh--enable-libtheora--enable-libtwolame--enable-libvidstab--enable-libvorbis--enable-libvpx--enable-libwebp--enable-libx265--enable-libxml2--enable-libxvid--enable-libzimg--enable-libzmq--enable-libzvbi--enable-lv2--enable-omx--enable-openal--enable-opencl--enable-opengl--enable-sdl2--enable-pocketsphinx--enable-librsvg--enable-libmfx--enable-libdc1394--enable-libdrm--enable-libiec61883--enable-chromaprint--enable-frei0r--enable-libx264--enable-sharedWARNING:libraryconfigurationmismatchavcodecconfiguration:--prefix=/usr--extra-version=0ubuntu0.22.04.1--toolchain=hardened--libdir=/usr/lib/x86_64-linux-gnu--incdir=/usr/include/x86_64-linux-gnu--arch=amd64--enable-gpl--disable-stripping--enable-gnutls--enable-ladspa--enable-libaom--enable-libass--enable-libbluray--enable-libbs2b--enable-libcaca--enable-libcdio--enable-libcodec2--enable-libdav1d--enable-libflite--enable-libfontconfig--enable-libfreetype--enable-libfribidi--enable-libgme--enable-libgsm--enable-libjack--enable-libmp3lame--enable-libmysofa--enable-libopenjpeg--enable-libopenmpt--enable-libopus--enable-libpulse--enable-librabbitmq--enable-librubberband--enable-libshine--enable-libsnappy--enable-libsoxr--enable-libspeex--enable-libsrt--enable-libssh--enable-libtheora--enable-libtwolame--enable-libvidstab--enable-libvorbis--enable-libvpx--enable-libwebp--enable-libx265--enable-libxml2--enable-libxvid--enable-libzimg--enable-libzmq--enable-libzvbi--enable-lv2--enable-omx--enable-openal--enable-opencl--enable-opengl--enable-sdl2--enable-pocketsphinx--enable-librsvg--enable-libmfx--enable-libdc1394--enable-libdrm--enable-libiec61883--enable-chromaprint--enable-frei0r--enable-libx264--enable-shared--enable-version3--disable-doc--disable-programs--enable-libaribb24--enable-libopencore_amrnb--enable-libopencore_amrwb--enable-libtesseract--enable-libvo_amrwbenc--enable-libsmbclientlibavutil56.70.100/56.70.100libavcodec58.134.100/58.134.100libavformat58.76.100/58.76.100libavdevice58.13.100/58.13.100libavfilter7.110.100/7.110.100libswscale5.9.100/5.9.100libswresample3.9.100/3.9.100libpostproc55.9.100/55.9.100GuessedChannelLayoutforInputStream#0.1 : stereoInput#0, avi, from 'tilemap.avi':Duration:N/A,start:0.000000,bitrate:N/AStream#0:0: Video: mjpeg (Baseline) (MJPG / 0x47504A4D), yuvj420p(pc, bt470bg/unknown/unknown), 1280x800 [SAR 1:1 DAR 8:5], 60 fps, 60 tbr, 60 tbn, 60 tbcStream#0:1: Audio: pcm_s32le ([1][0][0][0] / 0x0001), 48000 Hz, stereo, s32, 3072 kb/sStreammapping:Stream#0:0 -> #0:0 (mjpeg (native) -> vp9 (libvpx-vp9))Press [q] to stop, [?]for help[swscaler @ 0x55932cefff40] deprecated pixel format used, make sure you did set range correctly[libvpx-vp9 @ 0x55932cee54c0] v1.11.0Output#0, null, to '/dev/null':Metadata:encoder:Lavf58.76.100Stream#0:0: Video: vp9, yuv420p(tv, bt470bg/unknown/unknown, progressive), 1280x800 [SAR 1:1 DAR 8:5], q=2-31, 2000 kb/s, 60 fps, 60 tbnMetadata:encoder:Lavc58.134.100libvpx-vp9Sidedata:cpb:bitratemax/min/avg:0/0/2000000buffersize:0vbv_delay:N/Aframe=533fps=35q=0.0Lsize=N/Atime=00:00:00.00bitrate=N/Aspeed=0xvideo:0kBaudio:0kBsubtitle:0kBotherstreams:0kBglobalheaders:0kBmuxingoverhead:unknownOutputfileisempty,nothingwasencodedffmpegversion4.4.2-0ubuntu0.22.04.1Copyright (c) 2000-2021 the FFmpeg developersbuiltwithgcc11 (Ubuntu 11.2.0-19ubuntu1)configuration:--prefix=/usr--extra-version=0ubuntu0.22.04.1--toolchain=hardened--libdir=/usr/lib/x86_64-linux-gnu--incdir=/usr/include/x86_64-linux-gnu--arch=amd64--enable-gpl--disable-stripping--enable-gnutls--enable-ladspa--enable-libaom--enable-libass--enable-libbluray--enable-libbs2b--enable-libcaca--enable-libcdio--enable-libcodec2--enable-libdav1d--enable-libflite--enable-libfontconfig--enable-libfreetype--enable-libfribidi--enable-libgme--enable-libgsm--enable-libjack--enable-libmp3lame--enable-libmysofa--enable-libopenjpeg--enable-libopenmpt--enable-libopus--enable-libpulse--enable-librabbitmq--enable-librubberband--enable-libshine--enable-libsnappy--enable-libsoxr--enable-libspeex--enable-libsrt--enable-libssh--enable-libtheora--enable-libtwolame--enable-libvidstab--enable-libvorbis--enable-libvpx--enable-libwebp--enable-libx265--enable-libxml2--enable-libxvid--enable-libzimg--enable-libzmq--enable-libzvbi--enable-lv2--enable-omx--enable-openal--enable-opencl--enable-opengl--enable-sdl2--enable-pocketsphinx--enable-librsvg--enable-libmfx--enable-libdc1394--enable-libdrm--enable-libiec61883--enable-chromaprint--enable-frei0r--enable-libx264--enable-sharedWARNING:libraryconfigurationmismatchavcodecconfiguration:--prefix=/usr--extra-version=0ubuntu0.22.04.1--toolchain=hardened--libdir=/usr/lib/x86_64-linux-gnu--incdir=/usr/include/x86_64-linux-gnu--arch=amd64--enable-gpl--disable-stripping--enable-gnutls--enable-ladspa--enable-libaom--enable-libass--enable-libbluray--enable-libbs2b--enable-libcaca--enable-libcdio--enable-libcodec2--enable-libdav1d--enable-libflite--enable-libfontconfig--enable-libfreetype--enable-libfribidi--enable-libgme--enable-libgsm--enable-libjack--enable-libmp3lame--enable-libmysofa--enable-libopenjpeg--enable-libopenmpt--enable-libopus--enable-libpulse--enable-librabbitmq--enable-librubberband--enable-libshine--enable-libsnappy--enable-libsoxr--enable-libspeex--enable-libsrt--enable-libssh--enable-libtheora--enable-libtwolame--enable-libvidstab--enable-libvorbis--enable-libvpx--enable-libwebp--enable-libx265--enable-libxml2--enable-libxvid--enable-libzimg--enable-libzmq--enable-libzvbi--enable-lv2--enable-omx--enable-openal--enable-opencl--enable-opengl--enable-sdl2--enable-pocketsphinx--enable-librsvg--enable-libmfx--enable-libdc1394--enable-libdrm--enable-libiec61883--enable-chromaprint--enable-frei0r--enable-libx264--enable-shared--enable-version3--disable-doc--disable-programs--enable-libaribb24--enable-libopencore_amrnb--enable-libopencore_amrwb--enable-libtesseract--enable-libvo_amrwbenc--enable-libsmbclientlibavutil56.70.100/56.70.100libavcodec58.134.100/58.134.100libavformat58.76.100/58.76.100libavdevice58.13.100/58.13.100libavfilter7.110.100/7.110.100libswscale5.9.100/5.9.100libswresample3.9.100/3.9.100libpostproc55.9.100/55.9.100GuessedChannelLayoutforInputStream#0.1 : stereoInput#0, avi, from 'tilemap.avi':Duration:N/A,start:0.000000,bitrate:N/AStream#0:0: Video: mjpeg (Baseline) (MJPG / 0x47504A4D), yuvj420p(pc, bt470bg/unknown/unknown), 1280x800 [SAR 1:1 DAR 8:5], 60 fps, 60 tbr, 60 tbn, 60 tbcStream#0:1: Audio: pcm_s32le ([1][0][0][0] / 0x0001), 48000 Hz, stereo, s32, 3072 kb/sStreammapping:Stream#0:0 -> #0:0 (mjpeg (native) -> vp9 (libvpx-vp9))Stream#0:1 -> #0:1 (pcm_s32le (native) -> opus (libopus))Press [q] to stop, [?]for help[swscaler @ 0x562739e33040] deprecated pixel format used, make sure you did set range correctly[libvpx-vp9 @ 0x562739e14040] v1.11.0[libopus @ 0x562739e165c0] No bit rate set. Defaulting to 96000 bps.Output#0, webm, to 'tilemap-average.webm':Metadata:encoder:Lavf58.76.100Stream#0:0: Video: vp9, yuv420p(tv, bt470bg/unknown/unknown, progressive), 1280x800 [SAR 1:1 DAR 8:5], q=2-31, 2000 kb/s, 60 fps, 1k tbnMetadata:encoder:Lavc58.134.100libvpx-vp9Sidedata:cpb:bitratemax/min/avg:0/0/2000000buffersize:0vbv_delay:N/AStream#0:1: Audio: opus, 48000 Hz, stereo, flt, 96 kb/sMetadata:encoder:Lavc58.134.100libopusframe=533fps=3.2q=0.0Lsize=2600kBtime=00:00:08.86bitrate=2401.8kbits/sspeed=0.054xvideo:2479kBaudio:114kBsubtitle:0kBotherstreams:0kBglobalheaders:0kBmuxingoverhead:0.289294%
And on this ancient i7-4790K, it runs dog slow. It processed the 8.86-second-long video at 5% speed, dragging it out to almost three minutes. But the payoff is worth it. Instead of 321MB, the video file is down to 2.6MB:
Ever had the problem where you’re firing a bullet or some kind of projectile at high speeds, and it just goes right through the collision object, instead of hitting it? Here’s a weapon that fires a bullet at random velocities, to demonstrate the issue: The bullets impact the character in various places, rather than at …
Godot’s resources are quite powerful. However, modifying a resource class doesn’t automatically update any corresponding .tres files, unless you happen to edit a scene that uses that resource in some way. This doesn’t impact runtime behavior — the game still runs as expected. But it can impact version control and result in a messier diff …
We’re excited about this release, which includes a range of quality-of-life additions and refinements. Drag-and-drop support has been improved with a second strategy: Click-and-release: Simply clicking (and releasing) an item will pick it up. This leverages Godot’s force-drag functionality and approximates the drag-and-drop behavior that games like Valheim and Palworld use in their inventory items. …
This release addresses an issue that arose due to Godot 4.4 changes in how JSON is deserialized. Relevant Godot issues: #103374 and #61464. Makes sense why it was changed, but it might impact anyone unaware who’s consuming a JSON-based API, and wondering why responses may not be as expected. In addition, this release now includes character damage and …
Making videos for the web with Godot 4’s Movie Writer
Normally I use OBS for screen recording, but there are cases where it makes sense to use Godot’s built-in movie writer that was recently announced.
For example, if you have a slow PC or really demanding game, OBS will skip frames. It makes sense since OBS is just recording what’s on the screen in real time, while Godot has to do most of the work running the game. Recording with Godot directly will slow down the action at recording time, and you cannot hear any audio while that happens. But the video playback will end up being buttery smooth at 60 fps and include audio. It will also result in huge files. 321MB for 8 seconds, at 1280x800px (also Steam Deck’s native 16:10 resolution). Still, that’s not a video file I want to embed into any webpage or wherever as-is.
The best tool for converting video from the command line or programmatically is still ffmpeg. I’m running this on Windows in WSL2, on Ubuntu 22.04.
The best option for .webm seems to be the VP9 codec. Their docs provide some examples. I’ve tested a few parameters. For two-pass targeting an average bitrate, the target bitrate is specified with the -b:v switch:
This is what the output looks like:
And on this ancient i7-4790K, it runs dog slow. It processed the 8.86-second-long video at 5% speed, dragging it out to almost three minutes. But the payoff is worth it. Instead of 321MB, the video file is down to 2.6MB:
Related Posts
Projectiles going through collision objects
Ever had the problem where you’re firing a bullet or some kind of projectile at high speeds, and it just goes right through the collision object, instead of hitting it? Here’s a weapon that fires a bullet at random velocities, to demonstrate the issue: The bullets impact the character in various places, rather than at …
Update all resources after modifying a resource class
Godot’s resources are quite powerful. However, modifying a resource class doesn’t automatically update any corresponding .tres files, unless you happen to edit a scene that uses that resource in some way. This doesn’t impact runtime behavior — the game still runs as expected. But it can impact version control and result in a messier diff …
Inventory System v1.10 available
We’re excited about this release, which includes a range of quality-of-life additions and refinements. Drag-and-drop support has been improved with a second strategy: Click-and-release: Simply clicking (and releasing) an item will pick it up. This leverages Godot’s force-drag functionality and approximates the drag-and-drop behavior that games like Valheim and Palworld use in their inventory items. …
Inventory System 2 Alpha 5 available
This release addresses an issue that arose due to Godot 4.4 changes in how JSON is deserialized. Relevant Godot issues: #103374 and #61464. Makes sense why it was changed, but it might impact anyone unaware who’s consuming a JSON-based API, and wondering why responses may not be as expected. In addition, this release now includes character damage and …