Close Menu
    Trending
    • Gemini introducerar funktionen schemalagda åtgärder i Gemini-appen
    • AIFF 2025 Runway’s tredje årliga AI Film Festival
    • AI-agenter kan nu hjälpa läkare fatta bättre beslut inom cancervård
    • Not Everything Needs Automation: 5 Practical AI Agents That Deliver Enterprise Value
    • Prescriptive Modeling Unpacked: A Complete Guide to Intervention With Bayesian Modeling.
    • 5 Crucial Tweaks That Will Make Your Charts Accessible to People with Visual Impairments
    • Why AI Projects Fail | Towards Data Science
    • The Role of Luck in Sports: Can We Measure It?
    ProfitlyAI
    • Home
    • Latest News
    • AI Technology
    • Latest AI Innovations
    • AI Tools & Technologies
    • Artificial Intelligence
    ProfitlyAI
    Home » How to Use Gyroscope in Presentations, or Why Take a JoyCon to DPG2025
    Artificial Intelligence

    How to Use Gyroscope in Presentations, or Why Take a JoyCon to DPG2025

    ProfitlyAIBy ProfitlyAIApril 21, 2025No Comments21 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    Share
    Facebook Twitter LinkedIn Pinterest Email


    Picture by creator

    explores how browser-based computational notebooks — significantly the WLJS Pocket book — can rework static slides into dynamic, real-time experiences. This method isn’t restricted to displays; you may put together interactive lecture notes for college kids or colleagues and publish it on internet. For information scientists, physicists, it highlights new methods to speak fashions, simulations, and visualizations, making complicated concepts extra intuitive and fascinating.

    Is a PDF Sufficient?

    Animations, bells and whistles, particularly the type that have been well-liked in PowerPoint 15–20 years in the past, have largely taken a backseat. Add to this the compatibility points between LibreOffice and MS Workplace (even between variations for Home windows and Mac), the presence or absence of obligatory fonts — and the will to do one thing uncommon on the “stage” fades away shortly.

    Take a look at fashionable technical displays: very often, it’s only a PDF doc consisting of pages with vector and raster graphics, and typically GIF animations that eat up megabytes (like this put up), with no mercy.

    Unused Potential

    It’s value separating ornamental bells and whistles from those who carry further info in some media format. For instance, check out ECMA-363 [1] specification.

    Convert MATLAB Determine to 3D PDF / Picture by Ioannis F. Filippidis (fig2u3d handbook), BSD-2-Clause

    A 3D mannequin inside a PDF doc merely enhances the consumer/viewer expertise. You observe the article from totally different angles/cross-sections.

    It’s disappointing that such a function is nearly nowhere supported aside from Adobe Acrobat and certain is not going to be. It appears like we made a leap prior to now, however now we have now returned to static slides.

    Massive Scientific Convention DPG

    DPG-Frühjahrstagung  is a big European physics convention organized by the German Bodily Society (DPG) [2]. Yearly, they collect greater than 10^4 scientists and happen in German cities, overlaying an enormous array of physics fields.

    Deutsche Physikalische Gesellschaft / Picture by Wikimedia, PD-textlogo
    DPG2025 (Spring Assembly) came about within the great metropolis of Regensburg / Photograph by Tobi &Chris, Pexels License

    There are such a lot of displays, and it lasts virtually per week, so by the top, it turns into too overwhelming. However, this doesn’t diminish its worth as a platform for networking, training displays, and a dependable option to study what’s at present available on the market, which trains have gone already, and that are simply departing.

    The individuals in plenary classes are principally Grasp’s college students and PhD college students, with postdocs being rarer.

    Such a big and accessible platform is a superb motivation to strive one thing new 💡 even when one thing could go mistaken.

    What’s a JoyCon?

    Absolutely, the reader has seen units like this:

    A mean PPT clicker system / AI Generated “PPT Clicker” picture utilizing Dalle 3 by OpenAI

    This system acts as a slide switcher and typically as a laser pointer, connecting through Bluetooth or via a dongle. In any case, it’s a sort of controller with buttons. Controllers could be extra fascinating — just like the one from the 2017 Nintendo Swap handheld console

    JoyCon (R) / Picture tailored Wikimedia, PD

    It isn’t a lot greater, nevertheless it has some further cool options:

    • Analog stick 🕹️
    • 11 buttons ☎️
    • IR digicam 📸 (troublesome to make use of, no good API documentation)
    • Full IMU 🌐 (Inertial Measurement Unit) aka gyroscope with an accelerometer
    • Bluetooth connectivity; acknowledged as a daily HID

    The buttons can certainly be mapped to PowerPoint, or the stick can be utilized to manage slides, emulating mouse or keyboard clicks, because it was applied in these initiatives:

    I believed it will be cool to by some means use the IMU and analog stick. However for that, one would want to transcend PowerPoint and PDF 🧙🏼‍♂️

    Shifting Slides to Browser Surroundings

    The thought behind this isn’t new, nevertheless it’s necessary to do not forget that this method could not work for everybody. Nonetheless, by transferring the presentation show and creation to the browser (significantly Javascript and HTML lands), we mechanically acquire entry to all the probabilities of contemporary internet know-how: peripheral system help, JavaScript, CSS animation magic, and rather more, together with video. It’s necessary to notice that every one of that is cross-platform by default and can work virtually in all places.

    For instance, slides could be created in Markdown (or/and HTML) with the assistance of a easy framework (quite, a small library)  RevealJS [5]

    There may be additionally MDX-based presentation engines, and issues like Manim [6], Movement Canvas [7], however these guys require much more abilities to grasp.

    The RevealJS API is sort of easy, so controlling the slides through JavaScript instructions is simple to implement:

    setTimeout(() =>{
      Reveal.navigateNext(1);
    }, 1000)

    Nonetheless, this direct method has important drawbacks. It requires an web connection, and should you’d choose to keep away from it, you’ll want to make use of bundlers (comparable to Rollup) and embed all JavaScript libraries right into a single HTML file, as an example. Alternatively, you could possibly run a neighborhood internet server.

    Possibility with Jupyter Pocket book

    If you happen to like Python and IPYNB, then use nbconvert — it’s going to convert your pocket book immediately right into a RevealJS presentation, and also you gained’t even discover it! Or use the extension for Jupyter—RISE [8]

    Create Presentation from Jupyter Pocket book / Picture by creator

    In any case, the thought stays easy — we have to by some means enter the net browser surroundings to make the most of all the probabilities of JoyCon.

    Strive it on Binder!

    Possibility with WLJS Pocket book

    My opinion on WLJS [9] is likely to be considerably biased as I’m one in every of its builders (and lively customers). This open-source IDE with a pocket book interface is extra tightly built-in with the net surroundings, as slides will not be exported there however are as an alternative executed and are simply one other sort of output cell, alongside the acquainted Markdown.

    WLJS Pocket book / Picture by creator

    Beneath the hood, it additionally makes use of RevealJS however with just a few additional options:

    • It really works offline
    • It permits embedding interactive parts and elements, much like LaTeX Beamer
    • It’s built-in with Wolfram Language (freeware distribution)

    See extra about it in this story [10]. An final information on how you can make presentation there we printed in our official weblog: Dynamic Presentation, or How to Code a Slide with Markdown and WL [11].

    Let’s Dive into JoyCon

    So, the best choice is to make use of the already ready-made library joy-con-webhid [12]. Why spend time reinventing the wheel when individuals have already performed a terrific job for us?

    npm set up joy-con-webhid --prefix .

    All subsequent examples will probably be taken from the WLJS Pocket book. Nonetheless, you are able to do just about the identical factor utilizing Python + FAST API to interface with JavaScript or one thing related, and even simply use JS alone. The net model of the pocket book is on the market here [13].

    First, let’s take heed to what’s coming from the controller port.

    Code
    .esm
    import { connectJoyCon, connectedJoyCons } from 'joy-con-webhid';
    
    // Create join button
    const connectButton = doc.createElement('button');
    connectButton.className = 'relative cursor-pointer rounded-md h-6 pl-3 pr-2 text-left text-gray-500 focus:outline-none ring-1 sm:text-xs sm:leading-6 bg-gray-100';
    connectButton.innerText = "Join";
    let connectionState = "Join";
    let isJoyConConnected = false;
    let lastUpdateTime = efficiency.now();
    let isAllowedToConnect = false;
    // major handler operate (warning! known as at 60FPS)
    operate handleJoyConInput(element) {
      const currentTime = efficiency.now();
      if (currentTime - lastUpdateTime > 50) { // decelerate
        lastUpdateTime = currentTime;
        console.log(element);
      }
    }
    // JoyCon periodically goes to sleep, we have to wake it up
    const connectionCheckInterval = setInterval(async () => {
      if (!isAllowedToConnect) return;
      const connectedDevices = connectedJoyCons.values();
      isJoyConConnected = false;
      for (const joyCon of connectedDevices) {
        isJoyConConnected = true;
        if (joyCon.eventListenerAttached) proceed;
        await joyCon.open();
        await joyCon.enableStandardFullMode();
        await joyCon.enableIMUMode();
        await joyCon.enableVibration();
        await joyCon.rumble(600, 600, 0.5);
        joyCon.addEventListener('hidinput', ({ element }) => handleJoyConInput(element));
        joyCon.eventListenerAttached = true;
      }
      updateConnectionState();
    }, 2000);
    // Replace button state
    operate updateConnectionState() {
      if (isJoyConConnected && connectionState !== "Related") {
        connectionState = "Related";
        connectButton.innerText = connectionState;
        connectButton.type.background = '#d8ffd8';
      } else if (!isJoyConConnected && connectionState !== "Join") {
        connectionState = "Join";
        connectButton.innerText = connectionState;
        connectButton.type.background = '';
      }
    }
    // Deal with click on occasion
    connectButton.addEventListener('click on', async () => {
      isAllowedToConnect = true;
      if (!isJoyConConnected) {
        await connectJoyCon();
      }
    });
    // Simply decorations
    const container = doc.createElement('div');
    container.innerHTML = `<small>Presenter controller</small>`;
    container.appendChild(connectButton);
    container.className = 'flex flex-col gap-y-2 bg-white rounded-md shadow-md';
    // Return DOM factor to the web page
    this.return(container);
    // When a cell acquired eliminated
    this.ondestroy(() => {
      cancelInterval(connectionCheckInterval);
    });

    A very powerful operate right here is:

    operate handleJoyConInput(element) {
      const currentTime = efficiency.now();
      if (currentTime - lastUpdateTime > 50) { // decelerate
        lastUpdateTime = currentTime;
        console.log(element); //output to the console
      }
    }

    It seems to be like there are a lot of steps to do. In actuality, most of this code offers with connecting the controller and drawing a big “Join” button. Don’t pay an excessive amount of consideration to the particular strategies — they’ll simply get replaced with these obtainable in your particular surroundings:

    • this.return(dom) passes a DOMElement for embedding on the web page
    • this.ondestroy(operate) calls operate when the cell is deleted, to scrub up timers, and many others.
    • The primary line .esm is a option to specify the JavaScript cell subtype in WLJS Pocket book, which requires pre-bundling.

    Once we run this code cell, we’ll see the next:

    DOM Output Component / Picture by creator

    Then comply with these steps:

    • Disconnect the controller from the Nintendo Swap (System → Controllers → Disconnect).
    • Pair the JoyCon (R) with the PC by holding the small button on the facet.
    • Press “Join” on our presenter controller.

    Opening the browser console, we reveal the next messages:

    {
        "buttonStatus": {
            "y": false,
            "x": false,
            "b": false,
            "a": false,
            "r": false,
            "zr": false,
            "sr": false,
            "sl": false,
            "plus": false,
            "rightStick": false,
            "dwelling": false,
        },
        "analogStickRight": {
            "horizontal": "0.1",
            "vertical": "0.3"
        },
        "actualAccelerometer": {
            "x": 0,
            "y": 0,
            "z": 0
        },
        "actualGyroscope": {
            "dps": {
                "x": 0,
                "y": 0,
                "z": 0
            },
            "rps": {
                "x": 0,
                "y": 0,
                "z": 0
            }
        }
    }

    Numerous information! Let’s strive utilizing this for the good thing about our presentation 💡

    Buttons ☎️

    To start, we are able to use two buttons to modify slides

    Picture by creator

    Within the WLJS pocket book, slides will also be managed programmatically via a Wolfram wrapper operate that calls the RevealJS API.

    FrontSlidesSelected["navigateNext", 1] // FrontSubmit

    All that’s left is to set off this operate on the proper second when the button (or swap) is clicked. To do that, occasions have to be despatched from the Javascript world to the Wolfram machine, the place we are able to then do no matter we wish with them. This ends in the next diagram:

    Picture by creator

    You don’t have to consider this, since it’s seamlessly applied through APIs

    Let’s return to the code cell and modify the handler.

    Code
    //....
    //.......
    const buttonStates = { //all buttons states on JoyCon (R)
      a: false, b: false, dwelling: false, plus: false, r: false, sl: false, sr: false,
      x: false, y: false, zr: false
    };
    
    const joystickPosition = [0.0, 0.0];
    let restingJoystick = [0.0, 0.0];
    let isCalibrated = false;
    
    operate handleJoyConInput(element) {
      if (!isCalibrated) { //calibration
        restingJoystick = [Number(detail.analogStickRight.horizontal), Number(detail.analogStickRight.vertical)];
        isCalibrated = true;
        return;
      }
      const currentTime = efficiency.now();
      if (currentTime - lastUpdateTime > 50) {
        lastUpdateTime = currentTime;
        let buttonPressed = false;
        let joystickMoved = false;
        for (const key of Object.keys(buttonStates)) {
          if (!buttonStates[key] && element.buttonStatus[key]) buttonPressed = true;
          buttonStates[key] = element.buttonStatus[key];
        }
        const verticalOffset = Quantity(element.analogStickRight.vertical) - restingJoystick[1];
        const horizontalOffset = Quantity(element.analogStickRight.horizontal) - restingJoystick[0];
        if (Math.abs(verticalOffset) > 0.1 || Math.abs(horizontalOffset) > 0.1) {
          joystickMoved = true;
        }
        joystickPosition[0] = horizontalOffset;
        joystickPosition[1] = -verticalOffset;
        if (buttonPressed) {
          for (const key of Object.keys(buttonStates)) {
            if (buttonStates[key]) {
              server.kernel.io.fireplace('JoyCon', true, key);
              break;
            }
          }
        }
        if (joystickMoved) {
          server.kernel.io.fireplace('JoyCon', joystickPosition, 'Stick');
        }
      }
    }
    //.......
    //..

    As you may see, we have now added a number of objects right here:

    • Joystick calibration — analog sticks drift, so their digital place isn’t good 0.,0..
    • State of all buttons — why hammer the door each time should you solely want to softly knock when the state adjustments? This reduces system stress.
    • Sending states to the occasion pool — that is particular to WLJS, the place we ship information to the Wolfram machine (or Python should you’re in Jupyter).

    The final level seems to be like this (substitute with the equal in your surroundings):

    server.kernel.io.fireplace(String identify, Object state, String sample);

    Then, on the Wolfram facet, we are able to simply subscribe to those occasions like this

    EventHandler["name", {
      "pattern" -> Function[state,
        Print[state];
      ]
    }]

    That is very handy, as Javascript sends the names of the pressed buttons because the sample. On this case, you may instantly subscribe to slip switching, for instance, like this:

    • ZR — subsequent slide
    • Y — again

    Thus, programmatically controlling slides turns into intuitive:

    EventHandler["JoyCon", {
      "zr" -> (FrontSubmit[FrontSlidesSelected["navigateNext", 1]]&),
      "y" -> (FrontSubmit[FrontSlidesSelected["navigatePrev", 1]]&)
    }];

    Let’s Take a look at in Follow

    Let’s create a easy presentation. Begin typing with

    .slide
    
    # Slide 1
    
    __Hey Medium!__
    
    ---
    
    ![](https://add.wikimedia.org/wikipedia/en/7/7d/Lenna_percent28test_imagepercent29.png)

    Now, let’s join the JoyCon to the PC and hyperlink it to our Javascript script by urgent the Join button. Then, subscribe to the occasions as soon as within the lively session.

    Now, simply run the cell with the slides:

    The primary huge step in mastering JoyCon has been made! / Picture by creator

    Analog Stick 🕹️

    The stick theoretically permits controlling two sliders concurrently. For the DPG Spring Conferences, I had the thought of a stay demonstration of a really peculiar impact m𝒶𝑔𝒾c𝑎𝓁 𝓌𝑜𝓇𝒹𝓈 𝒻𝓇𝑜𝓂 𝓅𝒽𝓎𝓈𝒾𝒸𝓈. I imagine that some ideas are rather more impactful and understandable when demonstrated stay on stage.

    Here’s a condensed code snippet for the interactive widget:

    FaradayWidget := ManipulatePlot[
    Abs[(E^(I w (-1 + Sqrt[1 + (f/((-I g - w) w + (d - w0)^2))])) + E^(I w (-1 + Sqrt[1 + (f/((-I g - w) w + (d + w0)^2))]))) /. {g -> 0.694, w0 -> 50.0}]
    , {w, 20, 80}, {{f,2},0,100,1}, {{d,0},0,10,1}
    , FrameLabel->{"wavenumber", "transmission"}
    , Body->True
    ];
    FaradayWidget

    The interactive on-line model of this widget is available here [14]

    Picture by creator

    To embed it in a slide, insert its image as a tag (much like JSX):

    .slide
    
    # Faraday Widget
    Right here it's in motion
    <FaradayWidget/>

    Now, let’s hyperlink it to our stick

    Picture by creator

    To start with, let’s carry out a easy check and bind its place to a disk on the display screen:

    pos = {0.,0.};
    EventHandler["JoyCon", {"Stick" -> ((pos = #)&)}];
    
    Graphics[{
      Circle[{0,0}, 2.],
      Disk[pos // Offload, 0.1]
    }]
    Picture by creator

    Clearly, the actions are too abrupt. Furthermore, making small changes is kinda painful utilizing JoyCon. The answer? Integration!

    EventHandler["JoyCon", {"Stick" -> ((pos += 0.1 #)&)}];
    Picture by creator

    Now, let’s hyperlink the pos variable to the sliders of our widget:

    FaradayWidget := ManipulatePlot[
    Abs[(E^(I w (-1 + Sqrt[1 + (f/((-I g - w) w + (d - w0)^2))])) + E^(I w (-1 + Sqrt[1 + (f/((-I g - w) w + (d + w0)^2))]))) /. {g -> 0.694, w0 -> 50.0}]
    , {w, 20, 80}, {{f,2},0,100,1}, {{d,0},0,10,1}
    , FrameLabel->{"wavenumber", "transmission"}
    , Body->True
    , "TrackedExpression" -> Offload[5 pos] (* <-- *)
    ];

    Right here’s the way it seems to be stay on a slide:

    Picture by creator

    And within the precise DPG presentation:

    Picture by creator

    A Second of Relaxation

    Final yr, DPG came about in Berlin, and this yr — in Regensburg, which has about 23 instances fewer inhabitants and is 10 instances smaller in space. Nonetheless, the comfortable lands of Bavaria have all the time been nearer to my ❤️

    Picture by creator

    And that is the college. A stable 60s-style constructing. Wha 💪🏻

    Picture by creator

    A brand new invention — a cup “Drink and Eat Me”

    Picture by creator

    As a bonus, each drink will get a touch of waffle taste! However, be careful — don’t chunk into it whereas it’s stuffed with sizzling tea.

    I couldn’t take extra images since I acquired sick on the primary day and went again dwelling to Augsburg. Usually, spending six days at a convention is sort of difficult.

    Picture by creator

    Again to enterprise 🐏

    IMU or Gyroscope-Accelerometer Mixture 🌐

    To make use of them, we have to learn the corresponding fields from the particulars object, particularly:

    • actualAccelerometer: x, y, z
    • actualGyroscope: rps (radians per second)
    Code
    //..
    //....
    const buttonStates = {
      a: false, b: false, dwelling: false, plus: false, r: false, sl: false, sr: false,
      x: false, y: false, zr: false
    };
    
    const joystickPosition = [0.0, 0.0];
    let restingJoystick = [0.0, 0.0];
    let isCalibrated = false;
    let imuEnabled = false;
    // Allow IMU mode if allowed
    core.JoyConIMU = async (args, env) => {
      imuEnabled = await interpretate(args[0], env);
    };
    // Perform to deal with Pleasure-Con enter
    operate handleJoyConInput(element) {
      if (!isCalibrated) {
        restingJoystick = [Number(detail.analogStickRight.horizontal), Number(detail.analogStickRight.vertical)];
        isCalibrated = true;
        return;
      }
      const currentTime = efficiency.now();
      if (currentTime - lastUpdateTime > 50) { // Replace each 50ms
        lastUpdateTime = currentTime;
        let buttonPressed = false;
        let joystickMoved = false;
        for (const key of Object.keys(buttonStates)) {
          if (!buttonStates[key] && element.buttonStatus[key]) buttonPressed = true;
          buttonStates[key] = element.buttonStatus[key];
        }
        const verticalOffset = Quantity(element.analogStickRight.vertical) - restingJoystick[1];
        const horizontalOffset = Quantity(element.analogStickRight.horizontal) - restingJoystick[0];
        if (Math.abs(verticalOffset) > 0.1 || Math.abs(horizontalOffset) > 0.1) {
          joystickMoved = true;
        }
        joystickPosition[0] = horizontalOffset;
        joystickPosition[1] = -verticalOffset;
        if (imuEnabled) {
          server.kernel.io.fireplace('JoyCon', {
            'Accelerometer': Object.values(element.actualAccelerometer),
            'Gyroscope': Object.values(element.actualGyroscope.dps)
          }, 'IMU');
        }
        if (buttonPressed) {
          for (const key of Object.keys(buttonStates)) {
            if (buttonStates[key]) {
              server.kernel.io.fireplace('JoyCon', true, key);
              break;
            }
          }
        }
        if (joystickMoved) {
          server.kernel.io.fireplace('JoyCon', joystickPosition, 'Stick');
        }
      }
    }
    //....
    //..

    Since IMU will not be all the time wanted, the script features a boolean variable and a management operate JoyConIMU[True | False], permitting IMU measurements to be enabled or disabled.

    The JoyCon, like most different units with IMU (some smartphones, watches, however positively not VR headsets or quadcopters), contains:

    • 3-axis gyroscope — returns angular velocity in rad/sec round all three axes
    • 3-axis accelerometer — returns a single acceleration vector

    Query: Why can’t we use solely a gyroscope or an accelerometer?
    Let’s strive outputting each. First, allow IMU utilization:

    JoyConIMU[True] // FrontSubmit;

    Now, outline auxiliary capabilities and variables:

    prevTime = AbsoluteTime[];
    angles = {0,0,0};
    acceleration = {0,0,-1};
    
    course of[imu_] := With[{time = AbsoluteTime[]},
      With[{dt = time - prevTime},
        angles = (angles + {-1,1,1} imu["Gyroscope"][[{3,1,2}]] dt);
        acceleration = imu["Accelerometer"];
        prevTime = time;
      ]
    ]

    What occurs right here:

    • The accelerometer vector is solely saved in acceleration.
    • Gyroscope information is processed by:
      • Reordering angular velocity values (JoyCon {hardware} orientation) and adjusting instructions.
      • Integrating over time to acquire orientation angles

    Because of this, we receive:

    • Three angles defining JoyCon orientation angles.
    • One acceleration vector (at relaxation — the gravity course) acceleration.

    These three angles are conveniently expressed as a matrix (tensor):

    RollPitchYawMatrix[{[Alpha], [Beta], [Gamma]}] // MatrixForm

    Making use of this matrix to any 3D object permits it to be oriented in line with these angles. Bodily, on the JoyCon, it seems to be like this:

    Picture by creator

    It is very important observe that since we measure solely the primary by-product (utilizing Gyro), then the preliminary IMU orientation stays unknown. Due to this fact, we manually set the preliminary state, i.e.

    angles = {0., 0., 0}

    EventHandler["JoyCon", {
      "IMU" -> Function[val, 
        process[val];
      ]
    }];
    
    angles = {0,0,0}; (* calibration *)
    Refresh[acceleration, 0.25] (* dynamically replace *)
    Refresh[angles, 0.25] (* dynamically replace *)

    Actual-time Information Output:

    Picture by creator

    Properly… Not fairly apparent what these values imply. Let’s strive to attract then as vectors in 3D house:

    axis = Desk[{{0.,0.,0.}, Table[1.0 KroneckerDelta[i, j], {i,3}]}, {j,3}];
    
    EventHandler["JoyCon", {
      "IMU" -> Function[val, 
        process[val];
        axis[[1]] = {{0.,0.,0.}, RollPitchYawMatrix[angles].{0,1.0,0.0}};
        axis[[2]] = {{0.,0.,0.}, RollPitchYawMatrix[angles].{-1.0,0.0,0}};
        axis[[3]] = {{0.,0.,0.}, -Normalize[acceleration][[{2,1,3}]]};
        axis = axis;
      ]
    }];

    After which render them as coloured cones, the place:

    • Blue and purple — defines angles derived from the gyroscope information
    • Inexperienced — accelerometer information (inverted and normalized)
    {
      {Opacity[0.2], Sphere[]}, 
      Crimson, Tube[axis[[1]]//Offload, {0.2, 0.01}],
      Blue, Tube[axis[[2]]//Offload, {0.2, 0.01}],
      Inexperienced, Tube[axis[[3]]//Offload, {0.2, 0.01}]
    } // Graphics3D
    
    EventHandler[InputButton["Reset"], Perform[Null, angles *= .0]]
    Picture by creator

    The inexperienced vector is all the time aligned “accurately,” whereas the blue and purple vectors, representing gyroscope angles, accumulate errors over time, particularly with fast actions, inflicting drift.

    There are lots of methods to unravel this difficulty. The final thought is to regulate the angles utilizing accelerometer information (inexperienced vector), because the accelerometer exactly determines the downward course ( till an exterior power disturbs it).

    For a extra detailed clarification, take a look at a terrific video by James Lambert [15], which explores these issues and their options, together with a detailed example with Oculus DK1.

    Why the heck do we’d like this within the presentation?

    I requested myself this query once I found how deep the rabbit gap goes. In my speak on the magnetism session, there was precisely one slide the place the thought of an IMU made any sense:

    Picture by creator

    Do you see the crystalline construction? Discovering a “good” digicam angle for it’s certainly troublesome, so why not rotate it immediately, energetic? We don’t want all three angles and acceleration-just one will suffice.

    Thus, we discard the accelerometer and maintain solely the gyroscope:

    FrontSubmit[JoyConIMU[True]];
    timestamp = AbsoluteTime[];
    angle = 0.;
    rotation = RotationMatrix[angle, {0,0,1.0}];
    
    EventHandler["JoyCon", {
      "IMU" -> Function[val, 
         With[{angularSpeed = val["Gyroscope"][[1]], time = AbsoluteTime[], oldAngle = angle},
           angle += (time - timestamp) angularSpeed;
           timestamp = time;
         ];
         rotation = RotationMatrix[angle, {0,0,1.0}];
      ]
    }];

    Now, we simply want to use the rotation transformation tensor to our 3D construction. Since this crystal incorporates many ions, that are additionally coloured, I compressed them into base64

    base64 code
    CrystalRawStructure = "1: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" // Uncompress ;
    CrystalStructure = Graphics3D[
      GeometricTransformation[CrystalRawStructure, rotation // Offload]
    , ViewPoint->3.5{1.0,0.5,0.5}
    , ImageSize->{550,600}
    ]

    Let’s embed it into our slide:

    .slide
    
    # Slide
    
    Right here is my crystal construction!
    
    <CrystalStructure/>
    Picture by creator

    To make it much more handy, it will be helpful to subscribe to IMU solely when the slide is lively and unsubscribe when leaving it. That is straightforward to do as a result of RevealJS indicators the core on slide state change occasions. Let’s implement this as a element utilizing .wlx cell sort:

    .wlx
    
    InteractiveCrystalStructure := Module[{rotation = RotationMatrix[1Degree, {0,0,1.0}], id = CreateUUID[], timestamp = AbsoluteTime[], angle = 0., CrystalStructure},
      
      CrystalStructure = Graphics3D[
        GeometricTransformation[CrystalRawStructure, rotation // Offload]
        , ViewPoint->3.5{1.0,0.5,0.5}
        , ImageSize->{550,600}
      ];
      EventHandler[id, {
        "Slide" -> Function[Null,
          FrontSubmit[JoyConIMU[True]];
          EventHandler["JoyCon", {
            "IMU" -> Function[val, 
              With[{angularSpeed = val["Gyroscope"][[1]], time = AbsoluteTime[], oldAngle = angle},
                angle += (time - timestamp) angularSpeed;
                timestamp = time;
              ];
              rotation = RotationMatrix[angle, {0,0,1.0}];
            ]
          }];
        ],
        ("Destroy" | "Left")   -> Perform[Null,
          FrontSubmit[JoyConIMU[False]];
        ]
      }];
    <div>
        <CrystalStructure/>
        <SlideEventListener Id={id}/>
    </div>
    ]

    By putting this code on any slide, we obtain the specified end result with out polluting the worldwide house or interfering with different occasion handlers. Thus, IMU subscription administration is localized to the particular slide, and when switching slides, we accurately allow and disable information dealing with with out affecting different slides and their processing

    .slide
    
    # Earlier than
    
    ---
    
    # Slide
    
    Right here is my crystal construction!
    
    <InteractiveCrystalStructure/>
    
    ---
    # After

    These are precise slides from DPG2025:

    Picture by creator
    Quick video with my DPG2025 slides

    Ultimate Code and Pocket book

    The compiled presenter controller cell code is supplied underneath the spoiler. If inserted into an empty cell, it’s going to produce a useful widget for connecting a JoyCon.

    Compressed cells
    jsfc4uri1percent3AeJztfWtz28aWoGtmdqdq99NU7Q9oa3ZiUiJpAqRkmYqccWTnxlNx4vXjfvGqHJBoSkhAgAJAiaSjpercent2FRf7afpercent2FsnnO6Gpercent2BhuNEjKj3vvVN1UTAHd531Ovxpercent2B4P05fTpercent2Fpercent2Fh3r17percent2BTpercent2FBz09RXkzpercent2FEdpercent2Fpercent2BOpercent2Fw8zfN0EgVFlCYVyOtFzNpercent2Fgw7OgCN78v3percent2B5d6percent2FH89npercent2F7ntpercent2BNJunWcEpercent2BskmaJHxSpercent2FEe6OkuTjnrloUjI2S2bZumMPfgtXXUhs3vDx5dRpercent2BOAEieCpercent2Fhwpercent2FZWcaDgrMgCVlerGLOikuuCIE4bLwoijRBYEjMC5X1PSWzUxamk8WMJ0VvQoSexxzfWg8E3oP2iUStkHqTOMjzn4MZBpercent2FQHGY9B72vgucjyNOvO0ygpeMaydJGEPOzOQnbZPWLzuDtg86zrs4Ivi27Mp4V4usiCVfew32dTECQfpYsijhLeTdKEsyxKLroey2cjAl3mpercent2BBjzIMT0Izapercent2BENhevpercent2Fpercent2FAIWYEL9lbQAQx985E1l5pupgXmpneFGhEGw5holw440z5BqCmQZzzEgKsUbybh0DgbUQ2mfNsmmazIJnwXpLetNolUpercent2BECYVpimQP4R8xhLBgJuh02Lp8u0xkvXpercent2BYx2Ee9ZOVTHlePZaoguSxzVuXTWsEgyK0lGcRZXkST31percent2BleUTRc8re93v9DoOf81LhjANQcvEfEtgNFOVnQRyNs8Blsmi2eJ4E49iVlTpercent2BN4percent2FSGh29TaXIDREa9wGYvXr5jszTkLJqyQKAJZTLeE25DiFMW5KtkwlpBdpF3GEpercent2Bu2percent2Bz0ibK7IUtwE0QgA0bwPOPoIUJ63z8XeCem1UCSHxaJKGhFyi6hFIJUwLkLrIHMfFEg2FTBCAApGea2QmASxe1SmClr3ddNVpercent2BYwl9lpercent2FXszGPJNEekESxOnFG8x8HV1cFr3LNIvWaVIEcbvDtsBe8wxeAFK4EBlaLiyyBTpercent2BphCkWWSJfb5U9GJOVzCLLoBbZWB6Esjpk1y5KT9hhHwzAwMwilXEQcwWps1wJUit9GsWTSi4A5KrkvQJL5lboKQhVAl6m1xaEggJVQGxS83epercent2BYumUpercent2FTLpercent2BDcK0B295Sypercent2Fbbc150rV69ntAOGfffMOkS6q8hchr1percent2BQ1XMBYndppI7ES7VZXRuihfPpercent2FLdJpzLGpercent2B7xgq4zIrK917FSRCvgnBX8lrYOhjIOqYMUjDry6C47AXjvGUq0oYA6vc89scfrISwpZEwhqfsGDCsbpjPri9BOIC3eZw0gnsI3jWlrmlXVVCGlDnPABGiLkt43IvS3jTKeOuBqFwedHRQxh48nUx4zKEzwaFqezBSMXsdxAuel56YFIsgNkDbHYPMn1ZZmkpercent2FSOd9MogTrhfO8XVG47bAHUCMpercent2FaLvNidoaIW8opercent2FKkFT6erlRULZrtBMQw6yLp9YiKOoef0u5F2q6ncqKkRZndyrR1HYFUqPrZd5Zpercent2BSM9Sjr3gWpSFGW7xik0sO7Qjatex5qsYrrpercent2FUYgc8Zwrpercent2FAxhGcDpEL0areWrKJNVpW0ZjZ7XlbbzyMVkMJ8YxfRxPqG9k9YhVtZROysXcmYLS4percent2BY2AMXRsXpr9XTSNKkA6Dpercent2FJ70B4lBY4FOPQ1nxZFACYFXwJ1qLMWZsMhpercent2BhYSEcpG0qrcZeRxKu0QqkkYZOEPizhpercent2BCX2czdBQqrYDpercent2FZkac3BkE1gGFXPMW0d96M3RT7932DaUkIBBGD7XVW89gHEJ9XcgPFsfZSvEbikcGns97ROLrNOcpvm1YrSgZvpercent2FM7MRL1aCm8fv9fiV81YcQNQF794KNA2xW4VkbLpercent2BVIxOi0NbDRo7weMtCs26OLpercent2B6fVpercent2BIKHe1rENY9DAO7EgnKMbiz8BgwaHvbGweT3Cxqc4ejtX8Pj6TQ8fqCMyziUHVVw76jTbhp9XX0e6FGi13spercent2Fiq659P0kxg40RVt93FiPbQKHwHbVca7hihaublNqlhLlz5gIKEun0MIePmLlEsAYOdswegpercent2Bj62roLqCFhX98percent2Bpercent2FInwPv123wGTcATbGc5jdcRLktjaPipercent2FfSjyfrXwgznUWeHZZRSHLcNiNiNjfmAa8yXDnpercent2B4kjdlFMOpercent2Buuj4O3W8uIwgMbZogvwzC9AaeqvmN4jLKe6K1aJX022Z2Cvg5iL5qGY6Z4HAjLpunhkZMVhbtE8e0zdvVnLpercent2F5Lpercent2FBAdZYF8M84rxPl8zhYvflv8DyByncWZVma3WlW6Hpercent2F833v3IDoTwP4AA1nIyxpercent2Bakz0PQwjDh7MgvLiBIOz9lht100uZ3sqD2RxaAalWh6VzzKpercent2BaNfkOHlFP0Cvpercent2BeHuit7percent2BCxg8ZvwIwjwpercent2FYQ2aSldA0koIavCLWo1eg2O8NNaAoiaATHImxiwIN0xeQHGF6IOYYTkWJYdpercent2BJppuN9AKEdK76KFCHXXnwF1qlK1percent2Fpercent2BHeBfDTDjk2jpercent2BxlCjVAITJGhpPmXWpzpercent2Bpercent2BfiNaB2hHWxfLDrtYwb81FHl4DuA5WGtltmT1MwxrjeFjDlLlIGnuw7percent2BBkXX1LC1QBpercent2Fjjiz8D8WdogF37V0ADfj369ekXIKpercent2BHlD6k9CGlH9PzMT5f9TH3ysOUK8K6GlxVpercent2FIk32qp3yPZZqwuG3GcXSxhdgSnhaYVPA3xatw0kv0ICJxDOgcRZlzgrE2dg4xB1Tpercent2BAcSJyliTO0cQjSK7F9C4eq1VawpODpY8sUrLTntXg2xwGlx4AXou6Dc4ELIO7jzwGi7ZOj9percent2FdZF6TRuvQIfurwOSP0hpx1Uw76GIRApa76Zrqn0j0z3Vfpvpkpercent2BUOkDPX1I9Ic1percent2BkOiP6zRHxL9YY3percent2BMcEf1percent2BCPCf7YhscohHTyoMEYAxMzPJvSldDsqqYahi9mDGzdcuRACu4L7ANhHulNlYMMu8Kg6F2dgCcIkCzIpSvsKAkIo0kiIq8vAqQrsA6EWSSP6k3KIggHa51jaVxJVwrd14T269JIjr7g4RscfYOjXpercent2BM4KN0s9dSMUWrpS64iz7cNZZSXHIWFnwO03z7percent2BHKBepercent2Bpercent2FhzgPzgaeAqOYjoLgVIqCHHb8wZNOWIKq57KlomlNXK9PVMz8oc6Jmpercent2BlTnUM6tYLEf24MiDUynAvt0GVdWcp6D8TVCpercent2BghpsghooqOEGKMOBslCqapUCSYaRqpSvHA68anDfVYPzrhpcdpercent2BV03K3VHkpercent2ByNM8percent2FzDPoCU2KVtX2dtgYnsfwPF4bMpercent2BLYM2QfcaGDqvExNkpUjSPsaiSfsaGjCn8MlNYjpercent2BYzlS2At2W2DQBx6ZNnT5AL6ZT9k6ezFbPE6CA3BZvA8gpercent2BfZ2hzpercent2BQH9qHhWTS5zgE7OPMJ73CZVe86sMNKy1PVUTJ2gUsznWV82GgQYfxpercent2BkOPLpercent2BGJ2E7RLW3lH9X8upercent2FapoHDAaChSY9UJbxfh59kCnqS5i3EtiHyuYLIo6RF9qmBZDqIi8gKbTpDywHDLhgei2VmAS0JCP1cCiTYga0JFpercent2FjsozwHgsBEvNpz12Kt0fLh6rID9NsntTCU4B3hpercent2BY4wYFNoFenpercent2FWkCHM0vgxW9pgVaLo8lKt6sG2VPy7VcTNZolV7old0abzJu4iXh285o38dqAZMaLjkSGczPKmhi5cErP3IyEh0G5fbTnQelpercent2Fpercent2BIGAhgok1percent2FO7KiBkpercent2FmqkJVT4E5Wpercent2F1unniJpercent2Fr4E0xgAOkXWsTjuOtO9VHlukiHA9ZURfUyrzeZkRXvRtQgP4c4Jpercent2BleFuKt5V4W4m3tXhzdJpercent2BpUyhpOVuSU0Xb2Z6cKl7OVuVU8a7nGsNPewpx4yiwYQioW7fDQh4XAS4zvuETo1Gyx6A6IA6Sm9prMVdUCm2uHm6IlHqP5GsOUCpercent2BB8eXKSMKpercent2BLMpDf1fy71qMYkX6WPxi2pCeh2OR7x7rQrp8868sIcVAt08D3b4cpercent2Bg7Koa9HKR6liGEwETQGw2jmmRg5XqdRCENG8MlsZSes7QRtsDnTBpszNdjU3XWniQWjz60tqpercent2BtdzL8P4Ppercent2BWBvCGJNTLmC1Vx2K2Un2KmVOSWaMks0ZJZhunEjAy5WQCSWJnr4zslZ29NrLXZrZXEfdcxLpercent2FqNAZVA7p0NpqGWMNunozoi8mIvi2f5FbnhFVMmWHwaJzX8AQpr0bKE6S8GqnGmRChpEPBpimSy6Xqpercent2B5INuioKaKxpercent2FoJwuR34CTk4siBkCAeprCWs1TyCgUVT1PDBZY6zJmJTsBDVNkLUafsrwIl5dCScEkRJcabM7mgyrOl9sYcqOIY6sLpHuJRbKSwSpercent2FNBbtsSWijReloGpGRgqhSjCJrQs6UAOOymJpercent2BabEqU0guRbam5saqQKHMZs66yjFmcpercent2FpSWMxryYkgOWvTJ9sFy3blLQHRLpercent2BUj0GDVJoTxWhWWFtkMpercent2FmK13VVORXEFLYJsSU7Ip00percent2Bx8xWVpercent2BLKeBJEvbZOlUoAIZr0percent2BnLmAZPRlzJzWROLVDsgVXUCUloxv4dCExmjERUzfGIWbbO5percent2BpvNNVTVXguZlTNw8lEYDJubygaDzzRsZYmKZvpercent2BT7dqq3NQV8VV306db2VdFaKuZBzuY2dpercent2FFzBB4w3FZY7gN9BlW93RPfr7Vpercent2B5rVpercent2FS9l9YGIbWpercent2Br0f1NRtdN2VyGP6dm6GtUpercent2Fcpercent2BuGbwvY72percent2FT2mzv09ppercent2F0WmtMuZJ7F7aOQYEXZoUApercent2BjQV5UczIt1percent2FT2zYjhqHcJfzyai8KR7XqEEpQzSpercent2FJJ3percent2FOydVfBPMhyLrcUXAcZw1MZakcBGDjhN9UGA09OMSNchtt0dwIMQjpercent2FkOHpercent2FqHffZQ9Epercent2Fepercent2FWC8svpF9yF9WYxawVZhgPiCCwB1uClIeSRg0WsNtJqGpercent2FpoOyDu5yPkpercent2Bsyp2AuOk0SSaoswzHGogrpfjvetYSfyZqflQzUt8F3JYaRyD1SSXajMwNAUujWsMeNB0jK1kShop5dlbodJVXDPi6bUbc22Ok7NuMJOMU8uCpwxJzDQkCWLOAb1percent2BqAYpfUEyIkhkUT7rtmFDxXMiP0cpercent2FOwQcBLEk0UMwN8HBWCtfuLXPFaaaQEQY7rkntpercent2FQnLKAetpercent2FXdpercent2FJOQBB2PKqcR3i4percent2FWwKCu1ptaC5aZfwhi68GQpercent2BjxWwLpupercent2FCjNObLWieC22SRbQbfAtu34XLZpercent2FNi1YwY8mkAUefkehlkF1Fyseesxirr36qifVbuF8N9UkDiI6jD9n7Cg11yDpercent2FFeBwzD9mhTv5Y2gLRXWapR2JOVF9IdR0FOc0mPDsu0Ncpercent2FS71U6pFU5ORiKq4HfqxcQb75ZuRTp82rKvIWHRf5MO4hxIk7uUepercent2BwQNpercent2Fx3mFyuexFaJWSJL0BVs8gWI1jLCIzxBqiot8rohnPC2jBoHi0ELPrzMUiglutRtUpercent2BJheNU2Rpercent2BYgpDjaC2lqgrgbPppercent2BwybQTN55U5eU3K7LkEQzy9x3YF65soN1JurAR1UFqX5percent2BLDQzpercent2FmQuPeteVKNhtxPRt43qVOOTnwpiGNvE6UaK4lqBpDR8ZBsVVX5Gu2LYDZrYCuydL6rnfgiVNfF2O4YkNVGVbBRu3LsPT7GgOl6ThvvYzvarhpSSBgOpercent2Buzf2ONpercent2Bu1ekP0RLHraO2hBNDbgO1MGRgduhbuHIIYLsLpoSmKhktJ11EibeRNFSRENogL91NDFGFaD1tK6s4n2DpVusUsGvUdiWS8qioUhvaWStVpRFE0C9lZG1XlMWDYF6akLH9n9L9ZH29YV2GrwphjihrMjcgJWXOOpercent2Bpercent2Fwmn1Nc6y39y0nQ6sCMvFa1wxNQjTCoAUHYg4fblRvpUkc1AaBpercent2BXrKgEPnAI2OEnzDJY5qpVjV2WM1Uk9woztmVU3treQC3pAsrcUtT7uYKCpercent2Fa8EE0percent2BnvSv5d11aQDZJWh90q33rfpercent2BPO4q66iQdHF3DalPMEljnqYfSnZbY74jey34zbkP8OrgOuNF9Op2mJdSkDjhBpercent2FiNChaLX845N0j8DXSQGleJIV31OqL81GGo93CPeMXGef5K5694SBNpercent2BKUFhB7CkdcffIaErpercent2Fl1Gi9otpercent2FJXk9I7egx2fPTJUhItcXbpRTJNoZTeoAgdBvEWWGKOVpercent2BKguYTp5TFgtbzDDvMOoXh6ntmTmUbZ7CbIpercent2BMvgtzT7M89y4Pc6QEWJkEQHQb22OoxZQ42SDagwhvVrqAX0IuugMM4dmNLNgsnTMMTjgXXoIcjUbwIH2PeKpUGkBpercent2BPJ58HkstVK6FyqfopABpercent2B3NFpercent2FmlhAHvvCnwegJwZLln69Zkncpercent2BjszROsxfJu9yhmgdm8HwTpRxqfmQfwFujmsV9qF4percent2FXPKlO0eZX1ppercent2BBGRm6MRRk087bIaeGjU67rZDjhlZ3f330lvgxPOOZqKRbq7fUmhv9kZ7IJhhilHNMnRs9hQ3699aVVpercent2FTKJnCn85lvOZ4l8aLZxtLQKRDauY1SwSFtLJvaKXbohkkmyTETRpercent2FZRsmwi581SkQlxSERpdsSEakmSYzh9cbKQgNslIuKpUMumU7DxVHD2N4Grpercent2BmhS9CkTnUab2vtNzFAP00lW0STZqPNtTP4m22uAW6KBaeAnktAnWKzDXF6tfjCYg467Mglpkxfjdj3aRrjlJTHvlG47wfnbZrfVHmpercent2BnTeu8oZ2XlDlHdt5WZV3VENca7meX8MN05vELe4hZipercent2FmbnkpkzptbpkpHzuUbrFFdoPYlLmOm8Sm7FzX6shUCjcyOfMEpkZ44DdjDmrqQjOCl9O4bDXEfHF3jctaw9JadITdbbKhMpkFc2zDiAtznBpSpercent2FiSYQpercent2FngDWoKEDkT9qcsmjfZGQApercent2FpcQ9rW662FjQtBsxNpWzYUM5G9aE0wjuIBvO4TXIRxsAypercent2Bstqj7lpercent2B6Nz9gdTSO8fnYOxvMM2percent2Bpercent2FZbdixFMdBa2kUcOAP2percent2BDGu3dHqo191fcuOKW0ylLdkILbG58kTNgTWKuX4HHkOkVBXrYjpmOUVIThV6Pu4eNTIteYMmtxscshRhz12OUSmVpercent2Fp2KoGa3YSsdnAVza3e1VePDV95percent2Fbpercent2BQs5CR5S3Ppercent2B5ruEhPPTf56bHSnw3rGHT1G3Jpc9kMUF1t6glMCaRTXWdjrJV1QaRJDXACRbhbkWgI190SgTpercent2BS5percent2ByIixxZKUWwM6G314YZ60IMKz3MaRpercent2BbUXNZcB75ZjCfpbBYk4ZbxRK4BNkuGw9FDp2Qix5ZMp7pdRBh7xCtkt6OkJXyzwG5hNwlaEm12rTY5vrl7acyj03CdDnZtcLtbXpUD9Eb2ZJiTSBuHuatGTjhP8shtGZmzAycBSpzWjZweAZCzDSlzduAkQNu4I6DDNljQB5F8p15lznZuEnSzBX0QyXfqVebswEmAbragD3X3wDmGL3N24CRAt1twACINnHqVOdu5SdDNFhyASEOnXmXODpwE6GYLDmE4OXTW7WXOdk4SlCx4btepercent2BeiFvqjXKpercent2B8M21xjl2hzVFu8bShWo7TttVpercent2BaE83zkmpl2EsLhioB3zhO7cUQkOUMW53LcMw8qZzfxJPidxFM4G8SDKtJ31rNlzo7iCfC7iSdxIIo6rMG9A5zpercent2FcNqvzNlNQAlpercent2BJwEVTrP9BqDCwGmpercent2FMmdH8QT43cSTOBvEgpercent2Fp74GwEypwdxRPgdxNP4mxy7xDHs077lTm7CSjB7ySgwmm23xBUGDrtVpercent2BbsKJ4Av5t4EmeDeNC4DJ0tVJmzo3gCpercent2FG7iSRxwr9UIVPX2ph1hTpercent2Bu3UZp7LXKrOeBxMM95KOpercent2BBPeRdukbDwDC3salpercent2BZ40PdZHBYPqKndiYZ1KbBfNW64IWjC5wIxoOTTUpjEU8at7vRNDbRnB9R4Lpercent2BZoL1kVLNMNv9VTbdrcrLupercent2Fipgm720Zpercent2BM9p7U1dBqzuiwjSDedhA02LlIEndG6percent2Bapercent2B3mBKapercent2BVX3FBh6UCXSq8c6d65uKXCTvfPnVsnSKbXUXJxVnXEnN0lXKpercent2Bc0KXeDY3BccNyk8rIiyyIkpG54K1oiCVv7MwicH1Fu7a2J6SpFMJpercent2Fupercent2BxalosvUmNtpercent2B7KZwcprv0NaJjcuhxNb9vg0SvgbTsPYNCueInh5npercent2Fcp3mNZjXQ7jCpercent2FndHmfAO7Qelu6KJ7jpW8veZ4HF3TPoUzu7cnbLyV7n17Qcqpercent2BgLEU5VzemZjxP42u80p3percent2FRrej6qvP1SIhkMTdLnjn6lvxat53Jpercent2FZ3EqdexmfpNTcvMdyjBcuMZNpercent2FriKtftQVU85QECtJCWUm5lkNT7aISaH0Opercent2BaB6l8tuFgO0J124aAuN23kooyeEAy1xnyB0q8wLeq2j0cYlu9KfYnID5X4XJcXxU9yNLGmHVYjqqmpbyfFS4I7YSX2uXS4MyFkE4xDTpercent2B64bhpEFEsG50XOxmdzYM9KoiPupercent2B4Cpercent2FmzknMg0zFTBlKxqGiKqbKaDzUrpS5LZpercent2BUSLU7MneWR9x3qejkZeHDdUzLcpercent2Bpercent2F76r6apercent2Bvpercent2B9Xq8qmpercent2BeNWzCwEiqFwAty36b95aBPlzJsKPxGyRpercent2Bx99gPPD63qwAcQ3vDERvgONdkTPftQj308uwdVo6percent2B792ZJc4RulkORszzjzuC93A7c8percent2F3vLvzx7HDgO4X8prd8GXpercent2F94eDZiM7rAxtzPNlgToePr6rfsePG1kd4wrAEbD0af9LvfIbsT36NAk2pklaQC2ySEKq8F3i8esXyQpercent2F4percent2FYMCWqVpdHF4dldZHpercent2FsddgTuwOESPRlGw8bZx3E85B3Cwpercent2BFQypercent2FXoHpercent2F7QUB8fjnBpercent2Bq4QZNvjCG2IAHhHxQ0W40WSPpercent2Fbp38iLI0AA8S4L4VRpjSB4percent2BvbPuQkYRhn6zAH1dAFHJ1Auh6gpsBKPi4oTU420TQN3jTmi3gQxQ41MjW3tGkDaxe0SToMAdWyFVqTndnNFh43pnaCJbzqBH79ligutEgeyMswM2lopercent2Blk6HyLpBY30wZd1hggapdM5pKKJ247Rj40oXB4FhoSsKcUcPyNsjAmIaIUiTVmytb1XwxhwaoamHoVmABRcGGD2ZmtaWcpercent2BsLlMYsRHXvqqK3ZfbWVulpercent2Fuhu7bVzmJTpy4zV27Qh5vN9HkoOverRt9RAzYQJoetw6Ftja8BPtB3rMuikhvHMHYHDOqZlIXHuz8mlpercent2BBMQptP2ntKB1UgeYeuuq8qb3ShIMz3percent2F4MiOyc7d6mWpercent2Fjl1ki9C626K3afUoqcVAqJ7ry4d34kg4LSb21caW9np0vgRYAHptbJm5percent2BECDnuq9K49HC4tQ0ERl0GiNStFTq53GrPJHA9MnaQ9tbumOmxpnXOTOpercent2FXemo02KxtmjcObdohZpercent2BwJpercent2FKRoO6ui7bjpercent2FyeF29gXD7Xtz76QWcMauyjuFnMSkLZXkRp2JMpercent2Bx0Xo2BZwI1hd64gdcOwbej3Hpercent2BlAJSf2IhwBpercent2BSPL56JT2d8ZpWHOxwHX67apercent2B2LmcClepercent2B7bI5percent2BsOA6Tpercent2FFLqXX0r5TJWPaID2N64ydAupercent2FtM6fXtXpercent2BRd2sfevmM5Vpercent2B5G91NAxN0DRHQpercent2Fvtrpercent2BHsL6v5Fnfpercent2FbWguXK7mwJupercent2BrqLyNe4bqL178z20DOqzc1LCmkUXpercent2BRiMoeszwVMyNJTFGf6erlPem2JXvCWOGGXVhCeeffGP2percent2Bypercent2BuHihtBaOI4DDlnGDPomGswH2NJp33tYb77sT8O9CoI4percent2Bpercent2BFzpercent2BQ53ArcNj8rtVcXqDt8UseDJZddhldHGpveJlMlGxCHnNhzD8pCP24uwebWbHQ01L8hIdeoUU8ZFBSNWg2gLsZFshO3IXIUvNxxUhmkqmbwlWVpercent2F7QLRhTMVqezS1dhpercent2F3e8aPD4percent2BNDKHzpercent2BUc8percent2FPvIG2pQ8bQWucC3LHHu9R4feI9w45hpercent2F6vcNHvn90VCETolCfvtaDepercent2BblYeA4vfBbkL9P31DEpercent2B5YeHpercent2BEKo3Zj7FRdUuDEjXXco6FlJpercent2BEWIXPpPlHR2Lo9nc2NUkOo9q2sBGYYVfvgFWJ8i7J4j5xIrUpqBN3HaxtQ14Ev1X7IWrgvmsDm6Q1C4UoYauZtYugPnPw2s8Mf36b6KVLjdRBd5gpercent2BP7AKmIk6TRriQSLfJb4eWwpercent2BZBFhV4IF6gpercent2FRvzDafI7CeWW7rd2PBfJYBkLv4percent2BeaLZUSQdnOpBs4GFAPpercent2FjFPRpercent2BdHRc50QFjr7GCdH8DfMPD82y6Iss4skw4ppercent2BANMcC0iq2g3OSGOGU3E2gw3PUQECZTGldij6hROEKf76FQsWigwNDLaKCZ3gjpercent2BvQrKUFvdQ2percent2FTsOb8percent2BKn58percent2FEFpercent2FGSWsX68y8fXr97percent2Bf1Pz5u7h3bvwuyADLEx2LHz4Gh4oA9SirBx1aiukqGNmDTkIX0bEKPIw1MLiUsu3SAGmpMXfn1tC7dvTtnpercent2FaQlpercent2B7Apercent2FBeAguTpzj5rtxx1nv33fmbk2w1sxBUn2OUOaspVosLgXD0vCRYhyXykXHqSMn0KACPBWf8zMqgpercent2FvGJgj8rpercent2Blpercent2Bbdki1percent2BbLW3ZIKc54UsxRStQSs90nuORLufWeoGhHCZ7gpi4GPjgPlAul58Hkdy7OsRiHjkeuU9DCPpJdW5uoUZdmKQUMs9CNUkeDkTkppTGGgiPeOsZBt5HjoKspQUcpercent2FIjOqH4OzocX5lZFxaqZRJpercent2FzPpercent2FvjuraXVYDCyUobHOpercent2BlJx6lHpercent2BjluW1Z9dmzkOGZtw5unhkfOk8w1HnVrO8percent2FtbrD6T3TU1HnIbwPWa3GA1X3gzMZTB3xG1jGjjZ7DEqqNgBxbPppcE1Sh5Aghpercent2FRjNyHGwpxmpercent2BPM1SQ6sO29jY1ez8qHYpx0b1bzfaYnhcs0XDKRnXMRuT8YmLjrF33t50vwNpercent2BNqfvM2percent2Ffpercent2FGfsqNP30l33gEa77SYffgHyYTN5sGKdpercent2FKatpoJyQJTfB71lD7JxoXOlHtb4cN7A7qq8sYeZn2BpgQnoRIUKpF5139HJbkVBl1K71Y7I1rQaCV7W1kJcRA9REHTqrcrpercent2BJYugQaWZpJF1j561ikqITWo4yFX6W4T127nKR6BQvXRo3percent2BAZYhr7Hncvao0Nxq3edRD27VWlpercent2B7se3nliFEqx1prxCYpercent2Bu9WJfw3Ws6Wo89Gbkux4tmDSzMRoYB4Gm5WOFT70FWqqR2HYnrK6OpofVTQyjfB5Ar0LQwpercent2B7Q2SIv0pl4N9cfGxZfG9gbahr63U0EaxWqcUHOEMPeoSCPfZu7FOTmBSnNDhsUnBOOzW6p0OWKnXK21ur3qlW7DUCrXYDGuwAFuwDhVQpercent2B7wGW7AK13gsJbH3ZiWV4zcXKXIFKftjcCSFbHW0JHHUHpercent2FG4ydxXwXkpercent2BEdKLvAxeqpercent2Bgl1csAsg3SiyEpercent2Bed4mgnKHk7yK76percent2FoUi6Upercent2B4oB7EZpercent2BUNXHpercent2FFePoqSuKpercent2FXba3RUnIlpercent2FX9percent2FjwUpercent2BmOH8eEpercent2Bpercent2Bpercent2FcPH169epercent2F38wwe2percent2F1DeTD5vVVeSC9W12percent2BAw8YKrvU84X994YCAp95PlPRLml6llSrrqlj0xJ57lboTENLciRLfKmVQkgoKQWpercent2FDUNHIVGUEYPlP73LacdYhwPk1T0percent2BKIQGnMcaK49euPYIXStiP2Pz9G4S382j07Y8qiDvBzMOO3v2oMDFpercent2FRVsEo7JgrdEI6JVvbUlfsjpercent2FlaGkOIfXWlRYUCeuuqJcF1dIHD5R5EumMnjaQCxUlqpercent2FLGc9dK1LoPB3EDa3olHpTywaWCgm9percent2FFI0wnixmetaiTfpercent2FbLS6ypercent2BMC0NQh5WurhO5VDU49dlRGyYspcezcutkgqjvMixMTLUvKTbVuVupNuqupBWKbeqNootJq7ktRWgfZhmLyCOvGHpercent2FqDoEX2Qre478vZDgvEHdTNpercent2BZCY5RbLR9U2Km067ompercent2Bc6t5gARsU0Uchxw7s1pY8FhtJFbipercent2BBcO8w8TyTB4PcjZhRwDcVXpzxpercent2FE1ZW1sMpxM2VmF0Xf5b4ULVXpercent2FXfa1qWy3BNRB9bpercent2B3nrBZ0g9percent2BTF9qz1ur1n2N8hiZw9M0SxV7TJnQK3Sa1aj8DdoAtHC0xzc7GRs2m7QzN8fQtZM2y5percent2Fchs3wSMVhPKjsC3eQE9iCdW6IjpTmyBVT0zgZF8wZpercent2FHfCbGegSwJ5gYSD3qLgmXsb2Mx0EBpercent2FRz8UEeeZt15GiV45pdWN3nYnYXssnvE5nF3wOZZ12cF9LO62OETTxdZsOoe9vtsCoLko3RRxFHCu0macJqc6Hosn40IdJnjYywpercent2Btww0xxcC2percent2Bv39xxiRvCSvQVEFFNODOpercent2FZRSgSfVDuhIlyEWhnquUBqGkAoV5C4KyNpercent2BP6MPH8Lpercent2FcEpnlVIJvpnDehTDGWHV2yUlpercent2F4dCYod8To2X8UFfHqKuPpPT8nMV7x10Hi38pfm68p8XStos775TQbyqzSP5HHO99V6opercent2BhNAEByoSKpercent2BDhDlZ0FMM9kOM0azxXOKbUdWpercent2FjSO0xsevk2lGwyQSZrxnvASlIuqKgyyixzq0uRarw0NNqJkUbDOoQjhehoi0TlixNNKUnk4percent2BBJKacwlOpercent2ByVt0SP3LhNpercent2Fb6uqtFxrZnoZ7rpercent2BWBLp2esFverOtHaHbYFVd6q1qwVeypercent2BTaN5ut5uxWb3rlB3c2xrPxpRpercent2BC7NpF4QmeeTQpercent2FTmEUFA25FApbK1FMXuGdx1Y0KAgVjCpercent2Bhpercent2F1SH0M6g4unR6ugpvOUtvRCap07Jc3o2Hj49x8percent2FnSovrA1bKa9dkNSzMWJ3aaSOxesdC6KDcpercent2Bst0mtN89a5hAB6xAu59bdtlFVpercent2B7ktci0sFA23percent2BA5hRfOBjnLVOJNu4n6Xl4BWoJYUsiYewegOF3w9ql2eyqSmz8sumfNILTtpWuKbGhVVWJGNLlPAMkiLIs4XEvSnvTKOOtPVFX4OC9AmVsz1hG2BupGKXNcHlppercent2FfqKQ7tjkCnXHDaTKMFoEaeicNthe1Br7jXMqBvhbSj7qYVMp6uVi9ri3DZjous7yNpe1rGXI9yHwQ0tjbC6k0vt2AFrUlGp21N03percent2BtdjzNxsBsce003dNIJapercent2FHWcoyNZOtiN4htszaf6Bd1czkuycVpercent2BDHMALWOlrNM3dncMr8sONDje5qP38x30jEIrzU9dW32Ipercent2B7QoYOCJ3gDqUMNoKM298Lv1q3fqWTuBtA3hbjC5ffYIzwnTT7percent2BnbVWTQPVRvTbRVx51klMGjd2OGlmnGU2zy5AU31I5M3vB6gsoHearOyjKvk8Dgh6bdYdD82l3tPEqh70SYs8cEzd0yQHuxIJydPItpercent2FAaMvFjBuHscTH6percent2FoDEKsvnX8Hg6DYpercent2FLz7VV49n7d9ZpN42percent2Brj57J3a9Ypercent2BI4psVirLicE0nOHrgWUG4zNR0bEGDmd37MGSKI7ISm5ZuGpmF0XY1LBbSw249vXpercent2F4EeL9percent2Bm8percent2BCOH6CLRenweikHNtpercent2Fpercent2B1Dkpercent2FWrhB3OoS8KzyygOW4atbEbG4Hca8yXDnpercent2B4kjdlFMOpercent2BuYIQL49KbywjcrY2B88sgTGpercent2FgSbjmoipercent2FPKGK93Sqptpercent2FXMFHBzEHtlXk0zwb54XDYTDY2JMfU2percent2FYd79percent2B7lpercent2FwQpercent2FrxdQJpercent2BID7sp781percent2Fh4RfarGpBpercent2FDM8PIvyeRys3vwjPPN89v8BSeiR3Qpercent3Dpercent3D

    No analysis is required (don’t run it ;)). Merely disguise the enter cell, leaving solely the output seen via the properties (click on on the top-right nook of the group).

    Here’s a notebook with all examples [13] (a few of which work in a browser as nicely with no kernel).

    What if utilizing my very own laptop computer will not be an choice?

    The WLJS Pocket book could be exported to HTML, preserving even some dynamic behavior [16]. That is achieved via a fairly refined algorithm that tracks all occasion chains occurring on the JS and WL sides, making an attempt to approximate them utilizing a easy state machine. The result’s a regular HTML file containing all cells, slides, and the info of those state machines.
    Nonetheless, because of the number of values acquired from the IMU, the exporter can’t mechanically seize this in a JS state machine. Consequently, rotations and the joystick enter is not going to be preserved. Nonetheless, all the things else will operate as anticipated. 🙂

    … like a fish wants a bicycle

    Please consult with the ultimate sentence of that beautiful comics by
    Zach Weinersmith.

    If not for the pressing must showcase a rotating crystalline construction, this put up wouldn’t exist.

    Thanks on your consideration, and to those that learn this far 🧙🏼‍♂️ 🤍

    References

    [1] — ECMA-363 Specification, Wikipedia: https://en.wikipedia.org/wiki/Universal_3D
    [2] — DPG2025, Homepage: https://www.dpg-physik.de/
    [3] — Leo. Proper Pleasure-con Controller as a Distant, Hackster: https://www.hackster.io/leo49/right-joy-con-controller-as-a-presentation-remote-5810e4 (2024)
    [4] — Jen Tong, Nintendo Swap Pleasure-Con Presentation Distant, Medium: https://medium.com/@mimming/nintendo-switch-joy-con-presentation-remote-5a7e08e7ad11 (2018)
    [5] — RevealJS, Homepage: https://revealjs.com/
    [6] — Manim, Homepage: https://www.manim.community/
    [7] — Movement Canvas, Homepage: https://motioncanvas.io/
    [8] — RISE, Github Web page: https://github.com/damianavila/RISE
    [9] — WLJS Pocket book, Homepage: https://wljs.io/
    [10] — Vasin Ok. Reinventing dynamic and moveable notebooks with Javascript and Wolfram Language, Medium: https://medium.com/@krikus.ms/reinventing-dynamic-and-portable-notebooks-with-javascript-and-wolfram-language-22701d38d651 (2024)
    [11] — Vasin Ok. Dynamic Presentation, or Tips on how to Code a Slide with Markdown and WL, Weblog put up: https://wljs.io/blog/2025/03/02/ultimate-ppt (2025)
    [12] — Pleasure-Con WebHID, Github Web page: https://github.com/tomayac/joy-con-webhid
    [13] — JoyCon Presenter Software, On-line pocket book: https://jerryi.github.io/wljs-demo/PresenterJoyCon.html
    [14] — Faraday Impact, On-line pocket book: https://jerryi.github.io/wljs-demo/THzFaraday.html
    [15] — James Lambert VR powered by N64, Youtube video: https://www.youtube.com/watch?v=ha3fDU-1wHk
    [16] — Dynamic HTML, WLJS Documentation web page: https://wljs.io/frontend/Exporting/Dynamic%20HTML/

    All hyperlinks supplied have been visited on March 2025.



    Source link

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleAI is pushing the limits of the physical world
    Next Article Retrieval Augmented Generation (RAG) — An Introduction
    ProfitlyAI
    • Website

    Related Posts

    Artificial Intelligence

    Not Everything Needs Automation: 5 Practical AI Agents That Deliver Enterprise Value

    June 6, 2025
    Artificial Intelligence

    Prescriptive Modeling Unpacked: A Complete Guide to Intervention With Bayesian Modeling.

    June 6, 2025
    Artificial Intelligence

    5 Crucial Tweaks That Will Make Your Charts Accessible to People with Visual Impairments

    June 6, 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Top Posts

    When Physics Meets Finance: Using AI to Solve Black-Scholes

    April 18, 2025

    AI May Soon Help You Understand What Your Pet Is Trying to Say

    May 9, 2025

    OpenAI har lanserat en ”lightweight” version av deep research-verktyget

    April 28, 2025

    xAIs chatbot Grok lanserar Grok Studio med canvas-liknande funktion

    April 17, 2025

    3 Questions: Visualizing research in the age of AI | MIT News

    April 5, 2025
    Categories
    • AI Technology
    • AI Tools & Technologies
    • Artificial Intelligence
    • Latest AI Innovations
    • Latest News
    Most Popular

    Synthetic Data in AI: Benefits, Use Cases, Challenges, and Applications

    April 3, 2025

    10 Ways AI Can Improve Your Reading And Writing In 2025 » Ofemwire

    April 4, 2025

    Generating Data Dictionary for Excel Files Using OpenPyxl and AI Agents

    May 8, 2025
    Our Picks

    Gemini introducerar funktionen schemalagda åtgärder i Gemini-appen

    June 7, 2025

    AIFF 2025 Runway’s tredje årliga AI Film Festival

    June 7, 2025

    AI-agenter kan nu hjälpa läkare fatta bättre beslut inom cancervård

    June 7, 2025
    Categories
    • AI Technology
    • AI Tools & Technologies
    • Artificial Intelligence
    • Latest AI Innovations
    • Latest News
    • Privacy Policy
    • Disclaimer
    • Terms and Conditions
    • About us
    • Contact us
    Copyright © 2025 ProfitlyAI All Rights Reserved.

    Type above and press Enter to search. Press Esc to cancel.