{"id":1065,"date":"2026-03-19T01:29:25","date_gmt":"2026-03-19T01:29:25","guid":{"rendered":"https:\/\/www.world-machine.com\/blog\/?p=1065"},"modified":"2026-03-19T01:36:27","modified_gmt":"2026-03-19T01:36:27","slug":"custom-devices","status":"publish","type":"post","link":"https:\/\/www.world-machine.com\/blog\/2026\/03\/custom-devices\/","title":{"rendered":"Custom Devices"},"content":{"rendered":"\n<p>Another significant framework change is around how macro and code devices are handled. As mentioned in the kickoff, stuff that reads more like <em>What&#8217;s New?<\/em> should probably be written for the Help Center instead of here, but there&#8217;s a huge backlog of changes to write about and so some of that is just unavoidable. WIthout further ado:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Unifying Devices, Macros and Code<\/h3>\n\n\n\n<p>Dragontail does something I&#8217;ve long wanted: It makes both Macro devices and Code devices (&#8220;Custom&#8221; devices) <strong>first-class citizens<\/strong> within World Machine. <\/p>\n\n\n\n<p>What does that mean?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Macros, Code devices, and built-in devices all appear together in the Devices menu. (There is no more Macro menu). If they&#8217;re from your custom library, they gain a small badge but otherwise they are treated the same as builtins.<\/li>\n\n\n\n<li>Full versioning support for custom devices. If you create an updated version of your device and save it to the library, loading a world that uses that device will show an update available, and you can one-click-update.<\/li>\n\n\n\n<li>Code plugin devices can be saved into and browsed from your library just like a macro.<\/li>\n\n\n\n<li>All devices of any provenance can be favorited now and will show up together in a Favorites tab in the toolbar, menu, and quick-search dialog:<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.world-machine.com\/blog\/wp-content\/uploads\/2026\/03\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"642\" height=\"151\" src=\"https:\/\/www.world-machine.com\/blog\/wp-content\/uploads\/2026\/03\/image-2.png\" alt=\"\" class=\"wp-image-1067\" srcset=\"https:\/\/www.world-machine.com\/blog\/wp-content\/uploads\/2026\/03\/image-2.png 642w, https:\/\/www.world-machine.com\/blog\/wp-content\/uploads\/2026\/03\/image-2-300x71.png 300w\" sizes=\"auto, (max-width: 642px) 100vw, 642px\" \/><\/a><\/figure>\n\n\n\n<p>In support of that, I&#8217;m going to de-emphasize calling macros&#8230; well&#8230; Macros, inside of WM. It&#8217;s going to take a bit to change the habit \ud83d\ude42<\/p>\n\n\n\n<p>This change is quite new &#8211; the past week or so &#8211; and I still haven&#8217;t quite decided if custom devices should also appear in the toolbar categories with the builtins, if they&#8217;ve been assigned a family. I haven&#8217;t changed it yet but I&#8217;m leaning towards doing so. What do you think?<\/p>\n\n\n\n<p>This all reflects the fact that there&#8217;s not really any functional difference between a code device, macro, or even a built-in device. At the end of the day, how they do what they do is an internal detail.<sup data-fn=\"a6fc2425-24c0-4b52-9a7a-da3a4550c65b\" class=\"fn\"><a href=\"#a6fc2425-24c0-4b52-9a7a-da3a4550c65b\" id=\"a6fc2425-24c0-4b52-9a7a-da3a4550c65b-link\">1<\/a><\/sup>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Versioning and the content library<\/h4>\n\n\n\n<p>This is actually a bigger deal than it seems. Once you have a reusable component that you&#8217;ve polished up and started using elsewhere, it&#8217;s virtually impossible to change without builtin versioning support. <em>With <\/em>versioning, all you do is save the changed macro to the library and WM will either auto-update loaded worlds using that macro, or prompt, depending on how breaking the changes are &#8212; just like the builtin devices.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Compute languages<\/h4>\n\n\n\n<p>The initial code device implementation uses OpenCL for user-written compute kernels. In my experience, it works quite well. There&#8217;s only one problem &#8211; the world seems to have decided that OpenCL is deprecated \ud83d\ude41 . I knew this was the case during inital implementation, but unfortunately the GPU compute story is kind of a mess &#8211; there <em><strong>is <\/strong><\/em><strong><em>no<\/em> <\/strong>universally available gpu language for all platforms and manufacturers.<\/p>\n\n\n\n<p>That didn&#8217;t matter so much when I was just targeting Windows, where both AMD and NVidia have decent GPU drivers these days. But OpenCL is hard deprecated on Mac, and that presents a real problem to cross-platform custom devices.<\/p>\n\n\n\n<p>Now that Mac support is here, I needed a non-deprecated solution. I considered targeting WebGPU, which has a lot going for it even on the desktop. Then I ran across the Slang Shading Language (<a href=\"https:\/\/shader-slang.org\">https:\/\/shader-slang.org<\/a>). A modern project and now within the umbrella of Khronos, the folks responsible for OpenGL\/Vulkan, it brings considerably friendlier ergonomics for writing compute shaders than OpenCL, but cross-compiles to Vulkan\/Metal\/DirectX. <\/p>\n\n\n\n<p>Here&#8217;s the new equivelent &#8220;Hello World&#8221; computer shader in Slang.<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code>\/\/ Slang Compute Shader - Hello World\n\/\/ This shader generates a horizontal sine wave pattern\n\/\/ Resources are bound as entry point parameters - matched to Lua args by position\n\n&#91;shader(\"compute\")]\n&#91;numthreads(16, 16, 1)]\nvoid helloworld(\n    uint3 tid : SV_DispatchThreadID, \/\/ Thread ID\n    RWTexture2D&lt;float&gt; output,\n    uniform float waveSize\n)\n{\n    \/\/ No-op if outside of grid domain\n    uint width, height;\n    output.GetDimensions(width, height);\n    if (tid.x &gt;= width || tid.y &gt;= height) return;\n\n    \/\/ Calculate UV coordinates &#91;0, 1]\n    float2 uv = float2(tid.x, tid.y) \/ float2(width - 1, height - 1);\n\n    \/\/ Generate horizontal sine wave\n    float r = 0.5f + 0.5f * sin(uv.x * 3.14159f * 2.0f \/ waveSize);\n\n    \/\/ Write directly to texture\n    output&#91;tid.xy] = r;\n}<\/code><\/pre>\n\n\n\n<p>Compared to the equivelent opencl code, at least in my opinion it is friendlier to read. And as a strong bonus, it is almost completely compatible with HLSL or GLSL-flavored compute shaders, which removes a lot of the problems of using yet-another-language.<\/p>\n\n\n\n<p> There are a bunch more framework changes, but I think next time we&#8217;ll talk about some changes and new features in the 3D viewport.<\/p>\n\n\n\n<p>Stephen<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n<ol class=\"wp-block-footnotes\"><li id=\"a6fc2425-24c0-4b52-9a7a-da3a4550c65b\">There are a few observable differences.. Macros will tend to be less efficient than purpose-built devices, and GPU-based compute devices have a different set of memory restrictions and maximum grid sizes at play compared to CPU-based ones. But the point stands. <a href=\"#a6fc2425-24c0-4b52-9a7a-da3a4550c65b-link\" aria-label=\"Jump to footnote reference 1\">\u21a9\ufe0e<\/a><\/li><\/ol>\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Another significant framework change is around how macro and code devices are handled. As mentioned in the kickoff, stuff that reads more like What&#8217;s New? should probably be written for the Help Center instead of here, but there&#8217;s a huge backlog of changes to write about and so some of that is just unavoidable. WIthout [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"publish_to_discourse":"","publish_post_category":"20","wpdc_auto_publish_overridden":"1","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"39501","discourse_permalink":"https:\/\/forum.world-machine.com\/t\/custom-devices\/8421","wpdc_publishing_response":"success","wpdc_publishing_error":"","footnotes":"[{\"content\":\"There are a few observable differences.. Macros will tend to be less efficient than purpose-built devices, and GPU-based compute devices have a different set of memory restrictions and maximum grid sizes at play compared to CPU-based ones. But the point stands.\",\"id\":\"a6fc2425-24c0-4b52-9a7a-da3a4550c65b\"}]"},"categories":[25,2],"tags":[],"class_list":["post-1065","post","type-post","status-publish","format-standard","hentry","category-dragontail-peak","category-world-machine-development-news"],"_links":{"self":[{"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/posts\/1065","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/comments?post=1065"}],"version-history":[{"count":6,"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/posts\/1065\/revisions"}],"predecessor-version":[{"id":1073,"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/posts\/1065\/revisions\/1073"}],"wp:attachment":[{"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/media?parent=1065"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/categories?post=1065"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.world-machine.com\/blog\/wp-json\/wp\/v2\/tags?post=1065"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}