Short & Quick: Rendered STL-Model Viewer inside Topics - in under 2 minutes

Moved Tutorials
  • Re: Execute Custom JS on Infinite Scroll

    A little while ago I read this thread - today that strange white gave me an idea. How cool would it be to have an STL viewer right in the forum?

    So I got down to work and programmed a small snippet quick & dirty. Everything can be reached via custom codes in the ACP. No plugin required.

    Preview & Demo

    How does it work?

    Your liked files will be automatically converted:

    [My Model](

    turns out with a little 3d cube attached. A click on this cube toggles the STL-Model preview.

    Bildschirmfoto 2021-03-06 um 14.22.23.png

    As showed in the preview video, the STL-Viewer is also available in the composer preview.

    Bildschirmfoto 2021-03-06 um 14.24.18.png


    This little snippet is also mobile responsive 😵 You can view the 3d models on mobile devices like a boss. Full responsive. 💪


    Just at these snippets to custom HTML / CSS / Javascript via the ACP.


        min-width: 200px;
        max-width: 600px;
        width: 100%;
        min-height: 300px;
        max-height: 500px;
        height: 100%;
        border: 1px solid lightgray;
        border-radius: 10px;
        display: block;
        margin-bottom: 25px;
        margin-left: auto;
        margin-right: auto;
        -webkit-box-shadow: 0px 13px 29px -15px rgba(0,0,0,0.75);
        -moz-box-shadow: 0px 13px 29px -15px rgba(0,0,0,0.75);
        box-shadow: 0px 13px 29px -15px rgba(0,0,0,0.75);


    var re = /(?:\.([^.]+))?$/;
    var base_url = "";
    $(window).on('action:posts.loaded action:ajaxify.end action:composer.preview', function(data) {
          var href = $(this).attr("href");
          var ext = re.exec(href)[1];
          if(ext == "stl"){
            var indicator = Math.floor(Math.random() * 1000000) + 1 
            var currentModelUrl = $(this);
            if (href.substring(0, 8) !== 'https://'){
                href = base_url + href;      
            var alreadyLinked = $(this).closest('p').find('a#open-stl-model').attr('data-url');
                $(' <a style="margin-left: 5px; cursor: pointer;" title="3D-Modell anzeigen" id="open-stl-model" data-url="' + href + '" related="' + indicator + '"><i class="fa fa-cube"></i></a>').insertAfter(currentModelUrl);
                $(' <div id="' + indicator + '" style="display: none;" data-url="' + href + '"><iframe class="stl-model-viewer" src="' + href + '&color=white&bgcolor=transparent&shading=flat&rotation=yes&clean=yes&noborder=yes&orientation=top&edges=no"></iframe></div>').insertAfter( $(this).closest("p") );
    $(window).on('action:ajaxify.end action:composer.preview', function(data) {
            var thisLink = $(this);
            var related = thisLink.attr("related");
            $('div#' + related).slideToggle();

    Before running:

    Change the base url to fit your nodebb path:

    var base_url = "";

    In case of it would be:

    var base_url = "";

    It is used to convert local links e.g /assets/uploads/top_pla_3_holes.stl to public links:


    You can take a look at to adjust your model viewer inside of javascript.js.

    $(' <div id="' + indicator + '" style="display: none;" data-url="' + href + '"><iframe class="stl-model-viewer" src="' + href + '&color=white&bgcolor=transparent&shading=flat&rotation=yes&clean=yes&noborder=yes&orientation=top&edges=no"></iframe></div>').insertAfter( $(this).closest("p") );

    There are thinks like:

    • Shading
    • Rotation
    • Orientation
    • Model Color
    • Background Color


    The files which are linked in the topic have to be accessible by so private files wont work here I think. 🤔
    Maybe you do have to enable cross site origin for as well.

    This should be just a little playaround with some nodebb given features. As mentioned: it's a quick & dirty plugin which uses third party services. But there is also a standalone javascript plugin available: which you could use in a nodebb plugin.

    See this topic more as a simple feature and as well as an inspiration for other plugins in this direction you could make.

    Maybe its interesting for @Aleksei or someone else. 🙂

    Thanks for reading. 👋

  • @dogs ha, nice one! And how did you know that I may be interested? 🙂
    Thanks, indeed I may use it in future on our forum.

  • @aleksei I read your topic: and saw you're dealing with C4D-Files. 😄

Suggested Topics

  • 2 Votes
    4 Posts

    @dunlix yeah you should! 😂

    what does discord have to do with it? I tried using that link with different values and URL of course on my site, still didn't work. I'm sure it works for you, what am I missing?

    To be honest: Nothing.

    I am building a plugin for Discord integration..So the variable names are just placeholders if you want so.

    The important part is at the end of my main post.

    I hope that you find a slick way to use it for your community maybe. 🙂 👍

  • 0 Votes
    8 Posts
  • 0 Votes
    2 Posts

    Support you.
    Such great toy could be beneficial to your forum, but may be quite slow in China.

  • 0 Votes
    7 Posts

    Yes, as @Schamper recommends, do not use relative paths to break out of the plugin's root directory, just use module.parent.require to require from the context of plugins.js, or require.main.require to require from the context of app.js

    To answer OP, I use npm link to link my folders together. I have one big messy NodeBB install ( 😉 ) and next to it is a plugins/ folder where all the plugins live.

    @psychobunny integrated grunt into NodeBB, so I just use grunt --verbose to develop now, instead of ./nodebb dev 😄

  • 0 Votes
    14 Posts

    I was just going to let you take most of the credit. It's still your plugin, and you helped me make the modifications. I'm working on a new modification now, though. I'm trying to get the presentations to embed into an area of the user's profile.

    Right now, I'm testing it with the Fullname field on the profile, by entering the URL of the presentations (instead of a human name) and seeing if it parses into an embedded slide show.

    Would this be easy to accomplish? If not, are there any hints you could give me @a_5mith ?

    So far, I've edited the library.js file and replaced and with data.fullname and data.fullname.content. I've also tried changing the hook in plugin.js from:

    { "hook": "", "method": "parse", "callbacked": true } ]


    { "hook": "action:user.set", "method": "parse", "callbacked": true } ]

    I feel like I'm missing something, though. If you're up to it, I'd like to collaborate with you further.