<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.mxwendler.net/index.php?action=history&amp;feed=atom&amp;title=3._Python_plugin_reference</id>
	<title>3. Python plugin reference - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.mxwendler.net/index.php?action=history&amp;feed=atom&amp;title=3._Python_plugin_reference"/>
	<link rel="alternate" type="text/html" href="https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;action=history"/>
	<updated>2026-07-04T21:46:54Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.42.3</generator>
	<entry>
		<id>https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6090&amp;oldid=prev</id>
		<title>Admin at 20:34, 3 July 2026</title>
		<link rel="alternate" type="text/html" href="https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6090&amp;oldid=prev"/>
		<updated>2026-07-03T20:34:28Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 22:34, 3 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l36&quot;&gt;Line 36:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 36:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# The installation / resource directory: &amp;lt;code&amp;gt;plugins/media/python/&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# The installation / resource directory: &amp;lt;code&amp;gt;plugins/media/python/&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Playlist plugins:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Playlist plugins&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;, descending precedence&lt;/ins&gt;:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# The open &amp;lt;code&amp;gt;.mxw&amp;lt;/code&amp;gt; project&#039;s &amp;lt;code&amp;gt;plugins/playlist/python/&amp;lt;/code&amp;gt; tree (opt-in via settings, highest precedence)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# Directories configured explicitly in the settings (Python tab)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# The per-user data directory: &amp;lt;code&amp;gt;.../plugins/playlist/python/&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# The per-user data directory: &amp;lt;code&amp;gt;.../plugins/playlist/python/&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# The installation / resource directory: &amp;lt;code&amp;gt;plugins/playlist/python/&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# The installation / resource directory: &amp;lt;code&amp;gt;plugins/playlist/python/&amp;lt;/code&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6089&amp;oldid=prev</id>
		<title>Admin at 16:43, 3 July 2026</title>
		<link rel="alternate" type="text/html" href="https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6089&amp;oldid=prev"/>
		<updated>2026-07-03T16:43:35Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 18:43, 3 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l439&quot;&gt;Line 439:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 439:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| &amp;lt;code&amp;gt;plugin_video_writer&amp;lt;/code&amp;gt; || playlist || Write video files from the playlist || [https://github.com/mxwendler/plugin_video_writer plugin_video_writer]&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;| &amp;lt;code&amp;gt;plugin_video_writer&amp;lt;/code&amp;gt; || playlist || Write video files from the playlist || [https://github.com/mxwendler/plugin_video_writer plugin_video_writer]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;|}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;=== Developing a new plugin inside a project ===&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;The most convenient place to develop a plugin is &#039;&#039;&#039;inside a project folder&#039;&#039;&#039;: the plugin travels with the show, needs no write access to the installation, and overrides a same-named bundled plugin. Conceptually:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# &#039;&#039;&#039;Create a project folder&#039;&#039;&#039; — a plain directory that will hold everything belonging to the show, e.g. &amp;lt;code&amp;gt;D:/shows/particles/&amp;lt;/code&amp;gt;.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# &#039;&#039;&#039;Save the project file there&#039;&#039;&#039; — &#039;&#039;File → Save As..&#039;&#039; into that folder, e.g. &amp;lt;code&amp;gt;D:/shows/particles/particles.mxw&amp;lt;/code&amp;gt;. The folder containing the &amp;lt;code&amp;gt;.mxw&amp;lt;/code&amp;gt; file &#039;&#039;is&#039;&#039; the project folder; loading the project later makes MXWendler pick up its plugins again.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# &#039;&#039;&#039;Create the plugin directory there&#039;&#039;&#039; — the project mirrors the installation layout: &amp;lt;code&amp;gt;D:/shows/particles/plugins/media/python/&amp;amp;lt;plugin_folder&amp;amp;gt;/&amp;lt;/code&amp;gt; for media plugins (&amp;lt;code&amp;gt;plugins/playlist/python/...&amp;lt;/code&amp;gt; for playlist plugins). Enable &#039;&#039;Settings → Python → Check for plugins inside the project folder&#039;&#039; — project plugins have the highest precedence of all plugin locations.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;=== Letting Claude Code write the plugin ===&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;An AI coding agent like [https://claude.com/claude-code Claude Code] can write a complete plugin from the contract described on this page. Start it &#039;&#039;&#039;in the project folder&#039;&#039;&#039;, so it creates the plugin directly where MXWendler looks for it:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;cd D:/shows/particles&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;claude&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;/pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Give it the reference material and a precise task — for a generative OpenGL particle system for example:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Create an MXWendler media plugin under plugins/media/python/plugin_particle_system/&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;(folder + mxw_plugin.ini + mxw_main.py).&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Reference: the bundled example in &amp;lt;MXWendler install dir&amp;gt;/plugins/media/python/&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;plugin_opengl_cube_direct/ - follow its structure and hook usage exactly, and the&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;plugin howto at https://wiki.mxwendler.net/index.php?title=Python_Plugin_Howto.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;The plugin is a generative OpenGL particle system, addressed as&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;generative://particle_system, rendered with ModernGL directly into the media&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;texture via onRenderFrameGL (no readback, no onRenderFrame). Keep per-instance&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;state keyed by media_id. Emit particles from the center, integrate velocity and&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;gravity per frame scaled by the clip playback speed (onSetSpeed, onSpeedRange&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;-5..5). Expose particle count, gravity and start size as sliders in&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;onRenderPanel (mxw_imgui) and persist them via onSave/onLoad. Respect the GL&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;rules: attach with moderngl.create_context() lazily on the first render call,&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;never release the external texture, rebuild the fbo when (texture, w, h)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;changes, flip Y in the projection.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;/pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Then iterate: create the media in a clip (it appears under its &amp;lt;code&amp;gt;plugin_menu_name&amp;lt;/code&amp;gt;, or address &amp;lt;code&amp;gt;generative://particle_system&amp;lt;/code&amp;gt; directly) and refine with follow-up prompts (&quot;make the particles fade out&quot;, &quot;add a color gradient slider&quot;, ...). Errors show up in the interpreter console. For playlist plugins, &#039;&#039;Settings → Python → Reload Python Plugins&#039;&#039; hot-reloads the edited source; for media plugins, recreate the media (or restart) to re-import the module.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Persistence ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Persistence ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6088&amp;oldid=prev</id>
		<title>Admin at 16:31, 3 July 2026</title>
		<link rel="alternate" type="text/html" href="https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6088&amp;oldid=prev"/>
		<updated>2026-07-03T16:31:54Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 18:31, 3 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l113&quot;&gt;Line 113:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 113:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Installing own modules with mxw-pip ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Installing own modules with mxw-pip ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Plugins may use third-party packages (&amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;moderngl&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opencv-python&amp;lt;/code&amp;gt;, ...). &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;MXWendler bundles an &#039;&#039;embeddable&#039;&#039; Python 3.12 without its own &amp;lt;code&amp;gt;python.exe&amp;lt;/code&amp;gt;, so packages &lt;/del&gt;are not installed into the installation folder — &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;instead &lt;/del&gt;the &#039;&#039;&#039;mxw-pip&#039;&#039;&#039; helper (shipped next to the StageDesigner executable) installs them into the current user&#039;s writable folder, &#039;&#039;&#039;no administrator rights required&#039;&#039;&#039;:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Plugins may use third-party packages (&amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;moderngl&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opencv-python&amp;lt;/code&amp;gt;, ...). &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Packages &lt;/ins&gt;are not installed into the installation folder — the &#039;&#039;&#039;mxw-pip&#039;&#039;&#039; helper (shipped next to the StageDesigner executable) installs them into the current user&#039;s writable folder, &#039;&#039;&#039;no administrator rights required&#039;&#039;&#039;:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;pre&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l120&quot;&gt;Line 120:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 120:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;This is exactly the directory StageDesigner prepends to &amp;lt;code&amp;gt;sys.path&amp;lt;/code&amp;gt; when the option &amp;#039;&amp;#039;&amp;#039;Prepend user site-path&amp;#039;&amp;#039;&amp;#039; is enabled (Settings → Python tab).&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;This is exactly the directory StageDesigner prepends to &amp;lt;code&amp;gt;sys.path&amp;lt;/code&amp;gt; when the option &amp;#039;&amp;#039;&amp;#039;Prepend user site-path&amp;#039;&amp;#039;&amp;#039; is enabled (Settings → Python tab).&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;mxw-pip runs &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; with the &#039;&#039;&#039;Python 3.12 shipped with StageDesigner&#039;&#039;&#039; (&amp;lt;code&amp;gt;python.exe&amp;lt;/code&amp;gt; in the installation folder / in its &amp;lt;code&amp;gt;python312/&amp;lt;/code&amp;gt; subfolder) — nothing else needs to be installed. Only when the shipped interpreter is missing does it fall back to a system Python (&amp;lt;code&amp;gt;py -3.12&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;python&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;), which must be 3.12 so binary wheels match the runtime ABI.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Step by step:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Step by step:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# Make sure a system &#039;&#039;&#039;Python 3.12&#039;&#039;&#039; is installed — mxw-pip uses it to run &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; (install it via the StageDesigner dependencies installer, or from [https://www.python.org/ python.org] with &#039;&#039;Add python.exe to PATH&#039;&#039; ticked). It must be 3.12 so binary wheels match the embedded interpreter&#039;s ABI.&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# Open &amp;#039;&amp;#039;&amp;#039;Settings → Python&amp;#039;&amp;#039;&amp;#039; and enable &amp;#039;&amp;#039;&amp;#039;Prepend user site-path&amp;#039;&amp;#039;&amp;#039;. The two installer buttons below are only active while this option is on, because mxw-pip installs into exactly that folder. Enabling applies immediately; disabling takes full effect after a restart.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# Open &amp;#039;&amp;#039;&amp;#039;Settings → Python&amp;#039;&amp;#039;&amp;#039; and enable &amp;#039;&amp;#039;&amp;#039;Prepend user site-path&amp;#039;&amp;#039;&amp;#039;. The two installer buttons below are only active while this option is on, because mxw-pip installs into exactly that folder. Enabling applies immediately; disabling takes full effect after a restart.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# Click &amp;#039;&amp;#039;&amp;#039;Launch CMD window with mxw-pip module installer..&amp;#039;&amp;#039;&amp;#039; — this opens the installer shell (&amp;lt;code&amp;gt;mxw-pip-shell.bat&amp;lt;/code&amp;gt;). In the shell, type:&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# Click &amp;#039;&amp;#039;&amp;#039;Launch CMD window with mxw-pip module installer..&amp;#039;&amp;#039;&amp;#039; — this opens the installer shell (&amp;lt;code&amp;gt;mxw-pip-shell.bat&amp;lt;/code&amp;gt;). In the shell, type:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6087&amp;oldid=prev</id>
		<title>Admin at 16:27, 3 July 2026</title>
		<link rel="alternate" type="text/html" href="https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6087&amp;oldid=prev"/>
		<updated>2026-07-03T16:27:01Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 18:27, 3 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l111&quot;&gt;Line 111:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 111:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;mxw_imgui&amp;lt;/code&amp;gt; — Dear ImGui bindings for drawing settings panels inside MXWendler (used by &amp;lt;code&amp;gt;onRenderPanel()&amp;lt;/code&amp;gt;).&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;mxw_imgui&amp;lt;/code&amp;gt; — Dear ImGui bindings for drawing settings panels inside MXWendler (used by &amp;lt;code&amp;gt;onRenderPanel()&amp;lt;/code&amp;gt;).&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Installing &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Python packages &lt;/del&gt;===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Installing &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;own modules with mxw-pip &lt;/ins&gt;===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Plugins may use third-party packages (&amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;moderngl&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opencv&amp;lt;/code&amp;gt;, ...). &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Install &lt;/del&gt;them into the embedded interpreter &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;with the &lt;/del&gt;&#039;&#039;&#039;module installer &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;shell&lt;/del&gt;&#039;&#039;&#039; &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;available from &lt;/del&gt;the &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;settings dialog &lt;/del&gt;(&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Python tab&lt;/del&gt;), &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;which runs &lt;/del&gt;&amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;against MXWendler&lt;/del&gt;&#039;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;s Python 3&lt;/del&gt;.&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;12&lt;/del&gt;.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Plugins may use third-party packages (&amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;moderngl&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opencv&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;-python&lt;/ins&gt;&amp;lt;/code&amp;gt;, ...). &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;MXWendler bundles an &#039;&#039;embeddable&#039;&#039; Python 3.12 without its own &amp;lt;code&amp;gt;python.exe&amp;lt;/code&amp;gt;, so packages are not installed into the installation folder — instead the &#039;&#039;&#039;mxw-pip&#039;&#039;&#039; helper (shipped next to the StageDesigner executable) installs &lt;/ins&gt;them into &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;the current user&#039;s writable folder, &#039;&#039;&#039;no administrator rights required&#039;&#039;&#039;:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt; &lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;%APPDATA%\Python\Python312\site-packages&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;/pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt; &lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;This is exactly the directory StageDesigner prepends to &amp;lt;code&amp;gt;sys.path&amp;lt;/code&amp;gt; when the option &#039;&#039;&#039;Prepend user site-path&#039;&#039;&#039; is enabled (Settings → Python tab).&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt; &lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Step by step:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt; &lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# Make sure a system &#039;&#039;&#039;Python 3.12&#039;&#039;&#039; is installed — mxw-pip uses it to run &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; (install it via the StageDesigner dependencies installer, or from [https://www.python.org/ python.org] with &#039;&#039;Add python.exe to PATH&#039;&#039; ticked). It must be 3.12 so binary wheels match &lt;/ins&gt;the embedded interpreter&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&#039;s ABI.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# Open &#039;&#039;&#039;Settings → Python&#039;&#039;&#039; and enable &#039;&#039;&#039;Prepend user site-path&#039;&#039;&#039;. The two installer buttons below are only active while this option is on, because mxw-pip installs into exactly that folder. Enabling applies immediately; disabling takes full effect after a restart.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# Click &lt;/ins&gt;&#039;&#039;&#039;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Launch CMD window with mxw-pip &lt;/ins&gt;module installer&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;..&lt;/ins&gt;&#039;&#039;&#039; &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;— this opens &lt;/ins&gt;the &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;installer shell &lt;/ins&gt;(&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;code&amp;gt;mxw-pip-shell.bat&amp;lt;/code&amp;gt;&lt;/ins&gt;)&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;. In the shell, type:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;#: &amp;lt;pre&amp;gt;mxw-pip install &amp;lt;modulename&amp;gt;&amp;lt;/pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;#: for example:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;#: &amp;lt;pre&amp;gt;mxw-pip install moderngl&amp;lt;/pre&amp;gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;# &#039;&#039;&#039;Restart MXWendler StageDesigner&#039;&#039;&#039; so the new module is loaded.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt; &lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Other &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; subcommands are passed through unchanged, e.g. &amp;lt;code&amp;gt;mxw-pip list&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mxw-pip show moderngl&amp;lt;/code&amp;gt;&lt;/ins&gt;, &amp;lt;code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;mxw-&lt;/ins&gt;pip &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;uninstall moderngl&lt;/ins&gt;&amp;lt;/code&amp;gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;. The button &#039;&#039;&#039;Open per-user module install folder..&#039;&#039;&lt;/ins&gt;&#039; &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;next to the installer button shows the target directory in the Explorer. Both &amp;lt;code&amp;gt;mxw-pip.bat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;mxw-pip-shell.bat&amp;lt;/code&amp;gt; can also be run directly from the installation folder in any command prompt&lt;/ins&gt;.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt; &lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;Because the target folder lives in the user profile, every user keeps their own module set, and reinstalling / updating StageDesigner never touches the installed modules&lt;/ins&gt;.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Error handling ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Error handling ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l192&quot;&gt;Line 192:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 212:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Approach 2: rendering into an FBO and streaming ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Approach 2: rendering into an FBO and streaming ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Render with OpenGL (e.g. via &#039;&#039;&#039;ModernGL&#039;&#039;&#039;) into an offscreen framebuffer, read the pixels back and return them like in approach 1. The scene renders on the GPU, but every frame makes a round trip GPU → CPU (&amp;lt;code&amp;gt;fbo.read()&amp;lt;/code&amp;gt;) → GPU (PBO upload).&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Render with OpenGL (e.g. via &#039;&#039;&#039;ModernGL&#039;&#039;&#039;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;, installed once with &amp;lt;code&amp;gt;mxw-pip install moderngl numpy&amp;lt;/code&amp;gt;&lt;/ins&gt;) into an offscreen framebuffer, read the pixels back and return them like in approach 1. The scene renders on the GPU, but every frame makes a round trip GPU → CPU (&amp;lt;code&amp;gt;fbo.read()&amp;lt;/code&amp;gt;) → GPU (PBO upload).&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Bundled reference: &amp;lt;code&amp;gt;plugins/media/python/plugin_opengl_cube&amp;lt;/code&amp;gt; ([https://github.com/mxwendler/mxw-plugin-opengl-cube github]).&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Bundled reference: &amp;lt;code&amp;gt;plugins/media/python/plugin_opengl_cube&amp;lt;/code&amp;gt; ([https://github.com/mxwendler/mxw-plugin-opengl-cube github]).&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l428&quot;&gt;Line 428:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 448:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;#039;&amp;#039;&amp;#039;Errors&amp;#039;&amp;#039;&amp;#039; — open the interpreter console (IO dialog): hook exceptions are reported there (throttled). Remember that a throwing per-frame hook is blacklisted until reload.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;#039;&amp;#039;&amp;#039;Errors&amp;#039;&amp;#039;&amp;#039; — open the interpreter console (IO dialog): hook exceptions are reported there (throttled). Remember that a throwing per-frame hook is blacklisted until reload.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;#039;&amp;#039;&amp;#039;Media shows black&amp;#039;&amp;#039;&amp;#039; — &amp;lt;code&amp;gt;onRenderFrame&amp;lt;/code&amp;gt; returned a buffer of the wrong size (must be &amp;lt;code&amp;gt;width*height*4&amp;lt;/code&amp;gt; bytes), or a GL plugin created its own context instead of attaching to MXWendler&amp;#039;s (see the OpenGL rules).&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;#039;&amp;#039;&amp;#039;Media shows black&amp;#039;&amp;#039;&amp;#039; — &amp;lt;code&amp;gt;onRenderFrame&amp;lt;/code&amp;gt; returned a buffer of the wrong size (must be &amp;lt;code&amp;gt;width*height*4&amp;lt;/code&amp;gt; bytes), or a GL plugin created its own context instead of attaching to MXWendler&amp;#039;s (see the OpenGL rules).&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &#039;&#039;&#039;Missing packages&#039;&#039;&#039; — install them with &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;the module installer shell from the settings dialog; &lt;/del&gt;the embedded interpreter &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;has its own package directory, &lt;/del&gt;a system-wide Python installation &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;is not used&lt;/del&gt;.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &#039;&#039;&#039;Missing packages&#039;&#039;&#039; — install them with &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;mxw-pip (see [[#Installing own modules with mxw-pip|Installing own modules with mxw-pip]]) and make sure &#039;&#039;&#039;Prepend user site-path&#039;&#039;&#039; is enabled: &lt;/ins&gt;the embedded interpreter &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;does not see &lt;/ins&gt;a system-wide Python installation&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&#039;s packages&lt;/ins&gt;.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6086&amp;oldid=prev</id>
		<title>Admin: Created page with &quot;&lt;!-- MXWendler wiki page: How to create a Python plugin (media and playlist) --&gt; &lt;!-- Intended location: https://wiki.mxwendler.net/index.php?title=Python_Plugin_Howto --&gt; &lt;!-- See also: https://wiki.mxwendler.net/index.php?title=Python_Reference --&gt;  MXWendler StageDesigner can be extended with &#039;&#039;&#039;Python plugins&#039;&#039;&#039;. A plugin is a plain folder containing a manifest file and a Python module; MXWendler discovers it at startup, shows it in the user interface and calls a def...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.mxwendler.net/index.php?title=3._Python_plugin_reference&amp;diff=6086&amp;oldid=prev"/>
		<updated>2026-07-03T16:10:53Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;&amp;lt;!-- MXWendler wiki page: How to create a Python plugin (media and playlist) --&amp;gt; &amp;lt;!-- Intended location: https://wiki.mxwendler.net/index.php?title=Python_Plugin_Howto --&amp;gt; &amp;lt;!-- See also: https://wiki.mxwendler.net/index.php?title=Python_Reference --&amp;gt;  MXWendler StageDesigner can be extended with &amp;#039;&amp;#039;&amp;#039;Python plugins&amp;#039;&amp;#039;&amp;#039;. A plugin is a plain folder containing a manifest file and a Python module; MXWendler discovers it at startup, shows it in the user interface and calls a def...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;!-- MXWendler wiki page: How to create a Python plugin (media and playlist) --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Intended location: https://wiki.mxwendler.net/index.php?title=Python_Plugin_Howto --&amp;gt;&lt;br /&gt;
&amp;lt;!-- See also: https://wiki.mxwendler.net/index.php?title=Python_Reference --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
MXWendler StageDesigner can be extended with &amp;#039;&amp;#039;&amp;#039;Python plugins&amp;#039;&amp;#039;&amp;#039;. A plugin is a plain folder containing a manifest file and a Python module; MXWendler discovers it at startup, shows it in the user interface and calls a defined set of Python functions (&amp;#039;&amp;#039;hooks&amp;#039;&amp;#039;) at the right moments.&lt;br /&gt;
&lt;br /&gt;
Two plugin types exist:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Media plugins&amp;#039;&amp;#039;&amp;#039; produce pixels: they appear as a media source that can be placed in a clip like any video file. Examples: a web browser media, a generative OpenGL cube.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Playlist plugins&amp;#039;&amp;#039;&amp;#039; are items in the playlist grid: they run logic when their cue plays. Examples: advance the playlist at a given time of day, write video files, ping a host.&lt;br /&gt;
&lt;br /&gt;
The embedded interpreter is &amp;#039;&amp;#039;&amp;#039;Python 3.12&amp;#039;&amp;#039;&amp;#039;. The general Python command interface (module &amp;lt;code&amp;gt;mxw&amp;lt;/code&amp;gt;) is documented in the [[Python_Reference|Python Reference]].&lt;br /&gt;
&lt;br /&gt;
== Plugin anatomy ==&lt;br /&gt;
&lt;br /&gt;
A plugin is one folder with at least two files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
plugins/media/python/plugin_my_effect/&lt;br /&gt;
    mxw_plugin.ini      the manifest: identity, menu entries, addressing&lt;br /&gt;
    mxw_main.py         the Python module with the hook functions&lt;br /&gt;
    (more .py files)    optional: importable as &amp;quot;from plugin_my_effect.xyz import ...&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The folder name is the Python package name: additional modules in the folder are imported with absolute imports, e.g. &amp;lt;code&amp;gt;from plugin_my_effect.helpers import foo&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Plugin locations ===&lt;br /&gt;
&lt;br /&gt;
Plugins are searched in several directories. &amp;#039;&amp;#039;&amp;#039;User-controlled locations always override the bundled system location&amp;#039;&amp;#039;&amp;#039;, so a user can replace a built-in plugin by placing a same-named folder in a higher-precedence directory — even without write access to the installation.&lt;br /&gt;
&lt;br /&gt;
Media plugins, descending precedence:&lt;br /&gt;
&lt;br /&gt;
# The open &amp;lt;code&amp;gt;.mxw&amp;lt;/code&amp;gt; project&amp;#039;s &amp;lt;code&amp;gt;plugins/media/python/&amp;lt;/code&amp;gt; tree (opt-in via settings, highest precedence)&lt;br /&gt;
# Directories configured explicitly in the settings (Python tab)&lt;br /&gt;
# The per-user data directory, e.g. &amp;lt;code&amp;gt;%APPDATA%/MXWendler/plugins/media/python/&amp;lt;/code&amp;gt;&lt;br /&gt;
# The installation / resource directory: &amp;lt;code&amp;gt;plugins/media/python/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Playlist plugins:&lt;br /&gt;
&lt;br /&gt;
# The per-user data directory: &amp;lt;code&amp;gt;.../plugins/playlist/python/&amp;lt;/code&amp;gt;&lt;br /&gt;
# The installation / resource directory: &amp;lt;code&amp;gt;plugins/playlist/python/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The manifest: mxw_plugin.ini ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
[mxw_plugin]                            ; must be here&lt;br /&gt;
plugin_version = 1                      ; must be V1&lt;br /&gt;
plugin_script_language = Python         ; must be Python&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Common keys:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Key !! Used by !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_menu_parent&amp;lt;/code&amp;gt; || both || Menu the plugin is listed under (e.g. &amp;lt;code&amp;gt;Media&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;IO&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_menu_name&amp;lt;/code&amp;gt; || both || Display name in the menu&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_grid_name&amp;lt;/code&amp;gt; || both || Short name shown in the grid / media list&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_tooltip&amp;lt;/code&amp;gt; || both || Tooltip text&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_menu_level&amp;lt;/code&amp;gt; (media) / &amp;lt;code&amp;gt;plugin_action_level&amp;lt;/code&amp;gt; (playlist) || both || &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt; (default, visible), &amp;lt;code&amp;gt;development&amp;lt;/code&amp;gt; (visible in debug builds only), &amp;lt;code&amp;gt;disabled&amp;lt;/code&amp;gt; (invisible)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Media-only keys:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Key !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_media_scheme&amp;lt;/code&amp;gt; || The URI scheme this plugin handles, e.g. &amp;lt;code&amp;gt;generative&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;. Media is addressed as &amp;lt;code&amp;gt;&amp;amp;lt;scheme&amp;amp;gt;://&amp;amp;lt;payload&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_media_payload&amp;lt;/code&amp;gt; || Optional. If set, the plugin handles exactly &amp;lt;code&amp;gt;scheme://payload&amp;lt;/code&amp;gt;; if omitted, it handles every URI of its scheme. Several plugins can share one scheme and dispatch on the payload.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_media_create_uri&amp;lt;/code&amp;gt; || The URI used when the user creates this media from the menu / Create button.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Playlist-only keys:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Key !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_grid_bg_color&amp;lt;/code&amp;gt; || Item background color as &amp;lt;code&amp;gt;r g b a&amp;lt;/code&amp;gt; floats, e.g. &amp;lt;code&amp;gt;0.05 0.05 0.45 1.0&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== The plugin environment ==&lt;br /&gt;
&lt;br /&gt;
=== Per-instance state ===&lt;br /&gt;
&lt;br /&gt;
One Python module serves &amp;#039;&amp;#039;&amp;#039;many simultaneous instances&amp;#039;&amp;#039;&amp;#039; (several clips using the same media plugin, several copies of a playlist item). Before every hook call, the host sets module globals identifying the instance:&lt;br /&gt;
&lt;br /&gt;
* Media plugins: &amp;lt;code&amp;gt;media_id&amp;lt;/code&amp;gt; (integer)&lt;br /&gt;
* Playlist plugins: &amp;lt;code&amp;gt;item_id&amp;lt;/code&amp;gt; (integer) and &amp;lt;code&amp;gt;item_position&amp;lt;/code&amp;gt; (tuple &amp;lt;code&amp;gt;(x, y)&amp;lt;/code&amp;gt;, the grid position)&lt;br /&gt;
&lt;br /&gt;
The standard pattern is a module-level dictionary keyed by the id:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
storage = {}&lt;br /&gt;
&lt;br /&gt;
def onCreate():                    # playlist; media plugins use onOpen(uri)&lt;br /&gt;
    storage[item_id] = my_instance_state()&lt;br /&gt;
&lt;br /&gt;
def onDelete():&lt;br /&gt;
    storage.pop(item_id, None)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Host modules ===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mxw&amp;lt;/code&amp;gt; — the MXWendler command interface (playlist control, layers, clips, ...), see the [[Python_Reference|Python Reference]].&lt;br /&gt;
* &amp;lt;code&amp;gt;mxw_imgui&amp;lt;/code&amp;gt; — Dear ImGui bindings for drawing settings panels inside MXWendler (used by &amp;lt;code&amp;gt;onRenderPanel()&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Installing Python packages ===&lt;br /&gt;
&lt;br /&gt;
Plugins may use third-party packages (&amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;moderngl&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opencv&amp;lt;/code&amp;gt;, ...). Install them into the embedded interpreter with the &amp;#039;&amp;#039;&amp;#039;module installer shell&amp;#039;&amp;#039;&amp;#039; available from the settings dialog (Python tab), which runs &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; against MXWendler&amp;#039;s Python 3.12.&lt;br /&gt;
&lt;br /&gt;
=== Error handling ===&lt;br /&gt;
&lt;br /&gt;
Exceptions raised by a hook are reported to the interpreter console (IO dialog) — throttled, so a per-frame error does not flood the log. A &amp;#039;&amp;#039;&amp;#039;per-frame hook that throws is blacklisted&amp;#039;&amp;#039;&amp;#039;: it is not called again until the plugin is reloaded, so one broken callback cannot stall the render loop. Playlist plugins can be hot-reloaded from disk (state is snapshotted via &amp;lt;code&amp;gt;onSave()&amp;lt;/code&amp;gt;, the module source re-executed, then &amp;lt;code&amp;gt;onCreate()&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;onLoad()&amp;lt;/code&amp;gt; restore each instance), which also clears the blacklist.&lt;br /&gt;
&lt;br /&gt;
== Media plugins ==&lt;br /&gt;
&lt;br /&gt;
A media plugin is addressed by a URI: &amp;lt;code&amp;gt;&amp;amp;lt;scheme&amp;amp;gt;://&amp;amp;lt;payload&amp;amp;gt;&amp;lt;/code&amp;gt;, for example &amp;lt;code&amp;gt;generative://cube_spin_opengl&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;web://https://mxwendler.net&amp;lt;/code&amp;gt;. Every created media instance internally carries a unique instance token so two clips never share state; the plugin always sees the clean URI.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
All hooks are optional except &amp;lt;code&amp;gt;onOpen&amp;lt;/code&amp;gt; and &amp;#039;&amp;#039;&amp;#039;one of the two render hooks&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Hook !! Direction !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onOpen(uri)&amp;lt;/code&amp;gt; || returns &amp;lt;code&amp;gt;(width, height, length, fps, has_alpha)&amp;lt;/code&amp;gt; || Called when the media is created / loaded. Create the per-instance state here. Do &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; touch OpenGL yet (see below).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onRenderFrame(frame)&amp;lt;/code&amp;gt; || returns a pixel buffer || Approach 1 and 2: return a &amp;lt;code&amp;gt;H*W*4&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;uint8&amp;lt;/code&amp;gt; buffer (bytes / bytearray / numpy), &amp;#039;&amp;#039;&amp;#039;BGRA&amp;#039;&amp;#039;&amp;#039; byte order, top-down. The host uploads it into the media texture.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onRenderFrameGL(frame, texture, width, height)&amp;lt;/code&amp;gt; || returns &amp;lt;code&amp;gt;bool&amp;lt;/code&amp;gt; || Approach 3: render &amp;#039;&amp;#039;&amp;#039;directly into the media texture&amp;#039;&amp;#039;&amp;#039; — &amp;lt;code&amp;gt;texture&amp;lt;/code&amp;gt; is its raw GL handle. Return &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt; when the frame is in the texture; the pixel upload path is then skipped. Tried before &amp;lt;code&amp;gt;onRenderFrame&amp;lt;/code&amp;gt;; a plugin implements one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onClose()&amp;lt;/code&amp;gt; || — || The media is destroyed: release per-instance resources.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onRenderPanel()&amp;lt;/code&amp;gt; || — || Draw ImGui controls (via &amp;lt;code&amp;gt;mxw_imgui&amp;lt;/code&amp;gt;) in the clip panel, above the Video Info section.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onSizeChange(w, h)&amp;lt;/code&amp;gt; || — || The host changed the render size: recreate size-dependent resources.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onSpeedRange()&amp;lt;/code&amp;gt; || returns &amp;lt;code&amp;gt;(min, max)&amp;lt;/code&amp;gt; || Allowed clip playback speed range. Without it the media is locked to speed 1.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onSetSpeed(speed)&amp;lt;/code&amp;gt; || — || The clip playback speed changed. Not clamped: 0 and negative values are allowed.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onSave()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;onLoad(state)&amp;lt;/code&amp;gt; || string round-trip || Persist per-instance state in the project file.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onDisplayName()&amp;lt;/code&amp;gt; || returns string || Optional live display name (e.g. the web plugin reports the current page URL).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenGL rules for media plugins ===&lt;br /&gt;
&lt;br /&gt;
These rules apply to approaches 2 and 3:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Never create a standalone GL context&amp;#039;&amp;#039;&amp;#039; (no windowed/standalone &amp;lt;code&amp;gt;moderngl.create_context()&amp;lt;/code&amp;gt; at import or in &amp;lt;code&amp;gt;onOpen&amp;lt;/code&amp;gt;). MXWendler makes its own context current every frame; GL objects created in a foreign context will be dereferenced against the wrong context and crash the driver.&lt;br /&gt;
* Instead, &amp;#039;&amp;#039;&amp;#039;lazily attach on the first render call&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;moderngl.create_context()&amp;lt;/code&amp;gt; inside &amp;lt;code&amp;gt;onRenderFrame / onRenderFrameGL&amp;lt;/code&amp;gt; attaches to MXWendler&amp;#039;s context, which the host guarantees to be current there.&lt;br /&gt;
* The host &amp;#039;&amp;#039;&amp;#039;snapshots and restores all relevant GL state&amp;#039;&amp;#039;&amp;#039; around the render hooks (FBO bindings, program, VAO, buffers, viewport, enables, pixel store). The plugin does not need to unbind anything.&lt;br /&gt;
&lt;br /&gt;
=== Approach 1: rendering into pixels (CPU) ===&lt;br /&gt;
&lt;br /&gt;
The simplest approach: compute the frame on the CPU and return it. No OpenGL knowledge required. The host uploads the buffer into the media texture through a PBO (pixel buffer object) double-buffer, so the upload is asynchronous and cheap — the cost of this approach is the CPU rendering itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
W, H = 640, 360&lt;br /&gt;
storage = {}&lt;br /&gt;
&lt;br /&gt;
def onOpen(uri):&lt;br /&gt;
    storage[media_id] = {&amp;quot;phase&amp;quot;: 0.0}&lt;br /&gt;
    return (W, H, 1, 60.0, True)          # width, height, length, fps, has_alpha&lt;br /&gt;
&lt;br /&gt;
def onRenderFrame(frame):&lt;br /&gt;
    inst = storage[media_id]&lt;br /&gt;
    inst[&amp;quot;phase&amp;quot;] += 0.01&lt;br /&gt;
    # a moving horizontal gradient, BGRA byte order, top-down&lt;br /&gt;
    x = (np.linspace(0.0, 1.0, W) + inst[&amp;quot;phase&amp;quot;]) % 1.0&lt;br /&gt;
    row = np.zeros((W, 4), dtype=np.uint8)&lt;br /&gt;
    row[:, 0] = (x * 255).astype(np.uint8)     # B&lt;br /&gt;
    row[:, 2] = ((1 - x) * 255).astype(np.uint8)  # R&lt;br /&gt;
    row[:, 3] = 255                            # A&lt;br /&gt;
    return np.ascontiguousarray(np.tile(row, (H, 1, 1)))&lt;br /&gt;
&lt;br /&gt;
def onClose():&lt;br /&gt;
    storage.pop(media_id, None)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use this for: generated images, PIL/Pillow output, OpenCV results, slow-changing content.&lt;br /&gt;
&lt;br /&gt;
=== Approach 2: rendering into an FBO and streaming ===&lt;br /&gt;
&lt;br /&gt;
Render with OpenGL (e.g. via &amp;#039;&amp;#039;&amp;#039;ModernGL&amp;#039;&amp;#039;&amp;#039;) into an offscreen framebuffer, read the pixels back and return them like in approach 1. The scene renders on the GPU, but every frame makes a round trip GPU → CPU (&amp;lt;code&amp;gt;fbo.read()&amp;lt;/code&amp;gt;) → GPU (PBO upload).&lt;br /&gt;
&lt;br /&gt;
Bundled reference: &amp;lt;code&amp;gt;plugins/media/python/plugin_opengl_cube&amp;lt;/code&amp;gt; ([https://github.com/mxwendler/mxw-plugin-opengl-cube github]).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import numpy as np&lt;br /&gt;
import moderngl&lt;br /&gt;
&lt;br /&gt;
storage = {}&lt;br /&gt;
&lt;br /&gt;
class inst_t:&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        self.ctx = None&lt;br /&gt;
        self.fbo = None&lt;br /&gt;
&lt;br /&gt;
def onOpen(uri):&lt;br /&gt;
    storage[media_id] = inst_t()&lt;br /&gt;
    return (1024, 1024, 1, 60.0, True)&lt;br /&gt;
&lt;br /&gt;
def onRenderFrame(frame):&lt;br /&gt;
    inst = storage[media_id]&lt;br /&gt;
    if inst.ctx is None:&lt;br /&gt;
        # first frame: attach to MXWendler&amp;#039;s context (never earlier!)&lt;br /&gt;
        inst.ctx = moderngl.create_context()&lt;br /&gt;
        color = inst.ctx.texture((1024, 1024), 4)&lt;br /&gt;
        depth = inst.ctx.depth_renderbuffer((1024, 1024))&lt;br /&gt;
        inst.fbo = inst.ctx.framebuffer([color], depth)&lt;br /&gt;
        # ... build program / vao here ...&lt;br /&gt;
&lt;br /&gt;
    inst.fbo.use()&lt;br /&gt;
    inst.ctx.clear(0.0, 0.0, 0.0, 0.0, depth=1.0)&lt;br /&gt;
    # ... draw the scene ...&lt;br /&gt;
&lt;br /&gt;
    # read back: GL is bottom-up RGBA, the host wants top-down BGRA&lt;br /&gt;
    raw = inst.fbo.read(components=4, alignment=1)&lt;br /&gt;
    img = np.frombuffer(raw, dtype=np.uint8).reshape((1024, 1024, 4))&lt;br /&gt;
    img = np.flipud(img)&lt;br /&gt;
    return np.ascontiguousarray(img[:, :, [2, 1, 0, 3]])&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use this for: GPU-rendered content when you also need the pixels on the CPU (analysis, recording), or as a stepping stone to approach 3.&lt;br /&gt;
&lt;br /&gt;
=== Approach 3: rendering into a bound texture target (direct) ===&lt;br /&gt;
&lt;br /&gt;
The fastest path: the host passes the &amp;#039;&amp;#039;&amp;#039;raw GL handle of the media texture&amp;#039;&amp;#039;&amp;#039; to &amp;lt;code&amp;gt;onRenderFrameGL&amp;lt;/code&amp;gt;. The plugin attaches it to its own framebuffer and renders straight into it. &amp;#039;&amp;#039;&amp;#039;No readback, no upload, no texture streaming&amp;#039;&amp;#039;&amp;#039; — the frame never leaves the GPU.&lt;br /&gt;
&lt;br /&gt;
Bundled reference: &amp;lt;code&amp;gt;plugins/media/python/plugin_opengl_cube_direct&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import moderngl&lt;br /&gt;
&lt;br /&gt;
storage = {}&lt;br /&gt;
&lt;br /&gt;
class inst_t:&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        self.ctx = None&lt;br /&gt;
        self.fbo = None&lt;br /&gt;
        self.depth = None&lt;br /&gt;
        self.fbo_key = None       # (texture, w, h) the fbo was built for&lt;br /&gt;
&lt;br /&gt;
def onOpen(uri):&lt;br /&gt;
    storage[media_id] = inst_t()&lt;br /&gt;
    return (1024, 1024, 1, 60.0, True)&lt;br /&gt;
&lt;br /&gt;
def onRenderFrameGL(frame, texture, width, height):&lt;br /&gt;
    inst = storage[media_id]&lt;br /&gt;
    if inst.ctx is None:&lt;br /&gt;
        inst.ctx = moderngl.create_context()   # attach to MXWendler&amp;#039;s context&lt;br /&gt;
        # ... build program / vao here ...&lt;br /&gt;
&lt;br /&gt;
    # (re)wrap the host texture when the handle or size changed: the host&lt;br /&gt;
    # recreates the media texture on a render size change&lt;br /&gt;
    if inst.fbo_key != (texture, width, height):&lt;br /&gt;
        if inst.fbo:&lt;br /&gt;
            inst.fbo.release()&lt;br /&gt;
        if inst.depth:&lt;br /&gt;
            inst.depth.release()&lt;br /&gt;
        color = inst.ctx.external_texture(texture, (width, height), 4, 0, &amp;quot;f1&amp;quot;)&lt;br /&gt;
        inst.depth = inst.ctx.depth_renderbuffer((width, height))&lt;br /&gt;
        inst.fbo = inst.ctx.framebuffer([color], inst.depth)&lt;br /&gt;
        inst.fbo_key = (texture, width, height)&lt;br /&gt;
&lt;br /&gt;
    inst.fbo.use()&lt;br /&gt;
    inst.ctx.clear(0.0, 0.0, 0.0, 0.0, depth=1.0)&lt;br /&gt;
    # ... draw the scene; flip Y in the projection, see below ...&lt;br /&gt;
&lt;br /&gt;
    return True    # the frame is in the texture: host skips the upload path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rules specific to the direct approach:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Never &amp;lt;code&amp;gt;release()&amp;lt;/code&amp;gt; the external texture wrapper&amp;#039;&amp;#039;&amp;#039; — the GL texture belongs to MXWendler. Only release your own framebuffer and depth renderbuffer.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rebuild the framebuffer when &amp;lt;code&amp;gt;(texture, width, height)&amp;lt;/code&amp;gt; changes.&amp;#039;&amp;#039;&amp;#039; The handle is not stable: a render size change makes the host recreate the media texture.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flip Y.&amp;#039;&amp;#039;&amp;#039; MXWendler media textures are top-down (row 0 = top of the image), a GL render is bottom-up. Negate the Y row of your projection matrix (approaches 1/2 flip on the CPU instead, e.g. with &amp;lt;code&amp;gt;numpy.flipud&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Choosing an approach ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! !! 1: Pixels (CPU) !! 2: FBO + streaming !! 3: Direct texture&lt;br /&gt;
|-&lt;br /&gt;
| Scene renders on || CPU || GPU || GPU&lt;br /&gt;
|-&lt;br /&gt;
| Per-frame copies || CPU → GPU upload || GPU → CPU readback + CPU → GPU upload || none&lt;br /&gt;
|-&lt;br /&gt;
| OpenGL required || no || yes || yes&lt;br /&gt;
|-&lt;br /&gt;
| CPU access to the pixels || yes (you made them) || yes (after readback) || no&lt;br /&gt;
|-&lt;br /&gt;
| Typical use || generated images, PIL, OpenCV || GPU render that also needs CPU pixels || pure GPU generative content, best performance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Playlist plugins ==&lt;br /&gt;
&lt;br /&gt;
A playlist plugin is an item placed in the playlist grid. It participates in the cue lifecycle: it can run logic when its cue is played, every frame while its cue is active, or every frame globally. Playlist control functions (&amp;lt;code&amp;gt;mxw.playlist.play()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mxw.playlist.navigate_index()&amp;lt;/code&amp;gt;, ...) are documented in the [[Python_Reference|Python Reference]].&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Hook !! Required !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onCreate()&amp;lt;/code&amp;gt; || yes || A new instance was placed in the grid (also called after hot-reload): create the per-instance state entry.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onDelete()&amp;lt;/code&amp;gt; || yes || The instance was deleted: release the state entry.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onAction()&amp;lt;/code&amp;gt; || yes || The item&amp;#039;s cue was played (the item &amp;#039;&amp;#039;fires&amp;#039;&amp;#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onPostAction()&amp;lt;/code&amp;gt; || no || Called after the action completed.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onSave()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;onLoad(state)&amp;lt;/code&amp;gt; || yes || Serialize / restore per-instance state (string). Called on project save/load and around hot-reload.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onRenderPanel()&amp;lt;/code&amp;gt; || yes || Draw the item&amp;#039;s settings panel with &amp;lt;code&amp;gt;mxw_imgui&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;getText()&amp;lt;/code&amp;gt; || yes || Detail text appended to the item&amp;#039;s grid label.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onNewFrameAlways()&amp;lt;/code&amp;gt; || no || Called every rendered frame, regardless of cue state.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onNewFrameInPlayoutCue()&amp;lt;/code&amp;gt; || no || Called every rendered frame while the item&amp;#039;s cue is the active playout cue.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onPause(is_paused)&amp;lt;/code&amp;gt; || no || Pause was pressed / released.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onPreparePlayback()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;onCleanup()&amp;lt;/code&amp;gt; || no || Prepare / release playout resources.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onActivateInUI()&amp;lt;/code&amp;gt; || no || The item was selected in the user interface.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onActiveCueChange(direction)&amp;lt;/code&amp;gt; || no || The active cue changed past this item (navigation).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;onSettingsChanged()&amp;lt;/code&amp;gt; || no || Item settings were changed from outside.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;getDuration()&amp;lt;/code&amp;gt; || no || Report the item&amp;#039;s action duration in milliseconds.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;getTimeSinceOnActionIssued()&amp;lt;/code&amp;gt; || no || Report elapsed action time in milliseconds (drives the progress display).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;getColorBG()&amp;lt;/code&amp;gt; || no || Return a &amp;lt;code&amp;gt;mxw_imgui.Vec4&amp;lt;/code&amp;gt; (rgb 0..255) to set the grid cell background color dynamically.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;renderBlinking()&amp;lt;/code&amp;gt; || no || Return &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt; to make the grid cell blink.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Minimal example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import pickle, codecs&lt;br /&gt;
import mxw&lt;br /&gt;
import mxw_imgui&lt;br /&gt;
&lt;br /&gt;
class state_t:&lt;br /&gt;
    fire_count = 0&lt;br /&gt;
&lt;br /&gt;
storage = {}&lt;br /&gt;
&lt;br /&gt;
def onCreate():&lt;br /&gt;
    storage[item_id] = state_t()&lt;br /&gt;
&lt;br /&gt;
def onDelete():&lt;br /&gt;
    storage.pop(item_id, None)&lt;br /&gt;
&lt;br /&gt;
def onAction():&lt;br /&gt;
    # the item&amp;#039;s cue was played&lt;br /&gt;
    storage[item_id].fire_count += 1&lt;br /&gt;
&lt;br /&gt;
def getText():&lt;br /&gt;
    return &amp;quot; : fired %d times&amp;quot; % storage[item_id].fire_count&lt;br /&gt;
&lt;br /&gt;
def onRenderPanel():&lt;br /&gt;
    inst = storage[item_id]&lt;br /&gt;
    mxw_imgui.text_unformatted(&amp;quot;Fired %d times&amp;quot; % inst.fire_count)&lt;br /&gt;
    if mxw_imgui.button(&amp;quot;Reset&amp;quot;):&lt;br /&gt;
        inst.fire_count = 0&lt;br /&gt;
&lt;br /&gt;
def onSave():&lt;br /&gt;
    return codecs.encode(pickle.dumps(storage[item_id]), &amp;quot;base64&amp;quot;).decode()&lt;br /&gt;
&lt;br /&gt;
def onLoad(serialized):&lt;br /&gt;
    storage[item_id] = pickle.loads(codecs.decode(serialized.encode(), &amp;quot;base64&amp;quot;))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With a manifest:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
[mxw_plugin]&lt;br /&gt;
plugin_version = 1&lt;br /&gt;
plugin_script_language = Python&lt;br /&gt;
plugin_menu_parent = IO&lt;br /&gt;
plugin_menu_name = Fire Counter&lt;br /&gt;
plugin_grid_name = Counter&lt;br /&gt;
plugin_tooltip = Counts how often its cue was played&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bundled references: &amp;lt;code&amp;gt;plugins/playlist/python/plugin_advance_datetime&amp;lt;/code&amp;gt; (timed playlist navigation, rich settings panel — [https://github.com/mxwendler/plugin_advance_datetime github]), &amp;lt;code&amp;gt;plugin_video_writer&amp;lt;/code&amp;gt; ([https://github.com/mxwendler/plugin_video_writer github]), &amp;lt;code&amp;gt;plugin_ping&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Example plugins on GitHub ==&lt;br /&gt;
&lt;br /&gt;
The bundled example plugins are maintained as public repositories under [https://github.com/mxwendler github.com/mxwendler] — good starting points to fork for an own plugin:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Plugin !! Type !! Approach / purpose !! Repository&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_opengl_cube&amp;lt;/code&amp;gt; || media || Approach 2: ModernGL cube into an offscreen FBO, streamed || [https://github.com/mxwendler/mxw-plugin-opengl-cube mxw-plugin-opengl-cube]&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_opengl_cube_direct&amp;lt;/code&amp;gt; || media || Approach 3: same cube rendered directly into the media texture || ships with the installation&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_web_media&amp;lt;/code&amp;gt; || media || A web browser as media source (whole-scheme &amp;lt;code&amp;gt;web://&amp;lt;/code&amp;gt; plugin) || [https://github.com/mxwendler/mxw-plugin-web-media mxw-plugin-web-media]&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_advance_datetime&amp;lt;/code&amp;gt; || playlist || Timed playlist navigation with a rich settings panel || [https://github.com/mxwendler/plugin_advance_datetime plugin_advance_datetime]&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;plugin_video_writer&amp;lt;/code&amp;gt; || playlist || Write video files from the playlist || [https://github.com/mxwendler/plugin_video_writer plugin_video_writer]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Persistence ==&lt;br /&gt;
&lt;br /&gt;
Both plugin types persist per-instance state through the &amp;lt;code&amp;gt;onSave() → string&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;onLoad(string)&amp;lt;/code&amp;gt; pair. The string is stored inside the project file; anything goes, a common choice is base64-encoded pickle (see the playlist example above). Media plugins additionally have their render size stored and restored by the host automatically.&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Nothing appears in the menu&amp;#039;&amp;#039;&amp;#039; — check the manifest: &amp;lt;code&amp;gt;plugin_version = 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;plugin_script_language = Python&amp;lt;/code&amp;gt;, a non-empty &amp;lt;code&amp;gt;plugin_menu_name&amp;lt;/code&amp;gt;, and the level key (&amp;lt;code&amp;gt;development&amp;lt;/code&amp;gt; plugins only show in debug builds).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Errors&amp;#039;&amp;#039;&amp;#039; — open the interpreter console (IO dialog): hook exceptions are reported there (throttled). Remember that a throwing per-frame hook is blacklisted until reload.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Media shows black&amp;#039;&amp;#039;&amp;#039; — &amp;lt;code&amp;gt;onRenderFrame&amp;lt;/code&amp;gt; returned a buffer of the wrong size (must be &amp;lt;code&amp;gt;width*height*4&amp;lt;/code&amp;gt; bytes), or a GL plugin created its own context instead of attaching to MXWendler&amp;#039;s (see the OpenGL rules).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Missing packages&amp;#039;&amp;#039;&amp;#039; — install them with the module installer shell from the settings dialog; the embedded interpreter has its own package directory, a system-wide Python installation is not used.&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>