EEVblog® Electronics Community Forum
Electronics => Microcontrollers => Topic started by: Georgy.Moshkin on December 18, 2024, 03:44:43 pm
-
I like to use JavaScript for fast prototyping of DSP algorithms. The drawback is that I need to translate JavaScript to C each time to make it work on the STM32. I kind of get used to it, but it's pretty time consuming. I always use C-like syntax, splice()/push() translates to memmove(), etc. Most problems occur when I replace untyped let/var with C-types uint8_t/uint16_t/q31_t. Plus, I often use integer/fixed-point optimizations which are hard to implement in JavaScript with lots of Math.floor() and unsigned right shifts to obtain results close to the microcontroller version. I also tried to write directly in C with visualisation using Sciter.JS. This approach has another problem with endless maintaining of conversion code between native C/C++ and JavaScript data arrays. Why not writing directly on the MCU? Slow flashing, clunky visualization. I partially solved this on H7 by implementing serial-to-ram bootloader that accepts the binary using a custom utility executed in post-build step. Maybe there is a way to use a limited JavaScript syntax that can be translated to C automatically? But the problem is mainly with 64-bit internal number format of JavaScript numbers, and there is a lot of work with optimizing those data arrays and preventing overflows or memory waste for each particular firmware.
-
JavaScript is never going to be tiny, or fast on a microcontroller, but here is one that has been used for more than a decade at Samsung designed for largish ones: 64k RAM, >256k flash
https://github.com/jerryscript-project/jerryscript
-
But is the "final product" on MCU? Probably it is because why else you would be porting it there?
And if yes, it seems a bit backwards to let the prototyping flow limit and dictate the final output. I would try to reverse this, basically tackling the following questions:
* Why is writing DSP algorithms in C slow? Can you e.g. come up with better libraries (existing or your own), utility functions, headers, macros to help development?
* Why is Javascript needed to prototype on PC? Surely PCs run software written in C all the time. I understand the advantage of a browser, but can you come up with an alternative way to feed data into browser, or visualize from C with something else than browser?
I deal with a somewhat similar thing, developing algorithms when a final product is a resource constrained microcontrolled. I initially did algorithm prototyping in Matlab/Octave but I have reduced that to very simple proof of concepts (not something that would be complete enough to be ported) and go into C as early as possible. I spent some effort making the firmware compile as a PC version (e.g., replacing some Ethernet chip related stuff with OS socket stuff, and writing simple "simulator" functions for hw-like stuff. I spent some time to write generic enough CSV parsers for input data to enable long simulation runs bypassing the usual networking layer, and output CSVs which can be plotted in... drumroll, Matlab/Octave from where I started. This has enabled me to just jump into algorithm development in C directly and not think about porting - and worse, maintaining two copies of the algorithm in two different languages which need to be in sync.
-
I would prototype it on a desktop computer using test data in files. Make sure it's at least working correctly and doing what it's supposed to before even considering putting it on a microcontroller, because debugging it there will be significantly more difficult. Optimize it on the desktop, at least the things you mention. Then port it to a microcontroller. JS, however, is probably not fast enough, but once the basic functionality is correct as long as you don't wrap it into obscenely complicated, highly normalized functional code, just reimplementing it as C/C++, perhaps with select inline assembly, should be straightforward.
-
* Why is writing DSP algorithms in C slow? Can you e.g. come up with better libraries (existing or your own), utility functions, headers, macros to help development?
* Why is Javascript needed to prototype on PC? Surely PCs run software written in C all the time. I understand the advantage of a browser, but can you come up with an alternative way to feed data into browser, or visualize from C with something else than browser?
The slow part is updating of visualization code. With an HTML+JavaScript tab in a browser some changes can be made faster, and then I can try few smaller changes literally spending 3 seconds each by pressing F5 and looking into graphs/console. With the microcontroller tools there is a lag coming from building, flashing, looking for the output in serial monitor (e.g., responses to different commands). With a JavaScript it feels so much faster: I have notepad++ opened and a web browser tab + console on the second monitor. Make a change - F5 - ready.
It seems that development goes faster when the visualization and the algorithm both implemented using same language (JavaScript in this case). I tried STM32CubeMonitor multiple times, but the usability kind of worse than simply putting HTML buttons/canvases and using several JavaScript functions. Dynamic typing accelerates the development, but followed by a tedious conversion, very prone to bugs (overflows).
You are right about better libraries, maybe some custom solution to ease the visualization process, to exchange the data between browser and microcontroller using a more standardized way.
JavaScript is never going to be tiny, or fast on a microcontroller, but here is one that has been used for more than a decade at Samsung designed for largish ones: 64k RAM, >256k flash
https://github.com/jerryscript-project/jerryscript
I've seen this one. Interestingly, my search started from the opposite: I tried to find a C interpreter written in JavaScript, or something that allows to use C code directly from JavaScript functions.
I would prototype it on a desktop computer using test data in files. Make sure it's at least working correctly and doing what it's supposed to before even considering putting it on a microcontroller, because debugging it there will be significantly more difficult. Optimize it on the desktop, at least the things you mention. Then port it to a microcontroller. JS, however, is probably not fast enough, but once the basic functionality is correct as long as you don't wrap it into obscenely complicated, highly normalized functional code, just reimplementing it as C/C++, perhaps with select inline assembly, should be straightforward.
You are absolutely right. That's exactly what I am doing. Here is my workflow:
1. I create a simple firmware that streams sensor data to the serial port and saved to a computer file, very similar to audio recorder.
2. I load this file into my JavaScript template. I already have several templates from previous projects that allow to load and parse binary files. For example, some sensors provide 16-bit data, 32-bit data, signed/unsigned, etc. At this point I have one or several arrays filled with the recorded data
3. I start to create an algorithm. Again, have multiple snippets of code from previous projects that allow me to output 1D/2D data on HTML Canvas, scroll through it, display values under the cursor.
4. I test the algorithm on several recordings as shown in this video (https://youtu.be/GFkI5-jNdBA?si=xzSoM1dswpzVBsaC&t=63). I have used recordings for up/down/left/right and rotational gestures.
After algorithm is ready and tested on several recordings, the tedious part begins. I start to transfer the code from JavaScript to C.
At this point I use web serial api to stream recorded data back to the microcontroller to emulate sensor output. Resulting variables are streamed back to the browser and visualized along with JavaScript algorithm graphs. For example, I compare FFT output, that I have the same magnitudes. Or I compare correlation coefficient graph. If something is wrong, graphs will be different.
I get a very good working algorithm fast, but then I struggle with the "transfer" part. This video (https://youtu.be/GFkI5-jNdBA?si=xzSoM1dswpzVBsaC&t=63) shows before/after and scrolling through the data recordings using web browser.
Update: I re-read my topic and here is the summary:
- I quickly develop great algorithms on my PC in JavaScript, but then I waste a lot of time porting them to C for the microcontroller
- Know about MicroPython and open-source JavaScript interpreters, but these great tools come at a cost in terms of memory and performance requirements
- Familiar with Matlab/Octave, but it still requires rewriting the algorithm in C
- Pretty experienced with STM32CubeMonitor (Node-RED based), but I find it somewhat clunky. It would feel more natural if I could access visualization tools directly from the C source code
-
With the microcontroller tools there is a lag coming from building, flashing, looking for the output in serial monitor (e.g., responses to different commands).
My point was, compile the microcontroller code natively on PC. When only the algorithm module changes, building on a usual PC takes split second and there is no flashing to be done. This easily reduces iteration cost from 30-60 seconds to just a few.
When writing standard-compliant C and using stdint types, portability issues are rare, the exact same code will do exact same thing on PC and on MCU. (Of course final verification and testing is ultimately needed to prove this.)
-
I tried to find a C interpreter written in JavaScript, or something that allows to use C code directly from JavaScript functions.
You can consider emscripten (https://emscripten.org/) perhaps.
But it requires install toolchain and glue-code tho.
-
May I ask? why use JS?
-
I always use C-like syntax, splice()/push() translates to memmove(), etc.
You try to follow a C approach, but don't write in C? You are just trying to make your life hard.
I think most experienced people writing DSP code for MCUs do roughly what I do. I write in C. Not because it is best, but because its common to so many platforms. I take care that all my data types properly allow for compiling on both a PC and for the target MCU. I usually try to write a little code for the MCU target to capture data from the sensors, and store it in files on my PC. Then I can happily play around on the PC getting my code to process those captured files correctly. If the application is basically a serial flow I can usually just recompile for the MCU, perhaps have a little debugging of the differences between the code reading from files, and reading from the real sensors, and the application works. If there are control loops involved, additional work with the real time dynamics of the loop may be needed, debugging on the target, as that is the only place where real time is real.
-
The slow part is updating of visualization code. With an HTML+JavaScript tab in a browser some changes can be made faster, and then I can try few smaller changes literally spending 3 seconds each by pressing F5 and looking into graphs/console.
Write the code in C and compile it with MSVC++ or GCC. If you can't visualize in C and must use the JavaScript, let your C program create a file with output data and pass the file to the JavaScript. This won't take much longer and will eliminate double-language problems.
-
If you are extremely keen on the idea of using a browser interface, maybe even use libwebsockets (https://libwebsockets.org/) or similar; the PC build of your firmware would then act as web server and you could control it through the browser and get realtime data plotting on browser.
-
It is fairly easy to write a primitive HTTP server which just polls the IP stack for incoming data and when it sees some, it waits for say 100ms (to make sure a whole packet is available) and then fetches the packet and does a string compare for "GET..." and picks up the bit after it, so you can build a web form which acts on keyboard key inputs and mouse clicks. In the browser you need a bit of JS to make it all hang together, because a browser cannot just send keys to a web page, so JS is needed to convert a keyboard keypress into a web page request ("GET...").
The above (the timeout etc) is a hack but works perfectly in the context of getting a simple HTTP web page which accepts keystrokes, clicks on links, etc. It tends to break if there is other TCP/IP stuff going on ;)
I've done this on a 32F417 product I developed recently. All in GCC C. I don't know much about JS so I paid some guys on freelancer.com to product that, for peanuts. If you want I can send you the HTTP server source (PM me). It uses the Netconn API of LWIP, not sockets, but it should be trivial to change it to sockets. The web page is generated with C strings. Simple enough although there are some irritating gotchas because a browser can display only a properly formed web page, nothing less.
I believe there are other ways to implement a keyboard-server interface e.g. AJAX but I never played with that.
Some reading e.g. here
https://www.eevblog.com/forum/programming/what-is-the-http-server-client-interaction-to-do-file-transfers/ (https://www.eevblog.com/forum/programming/what-is-the-http-server-client-interaction-to-do-file-transfers/)
Much better to do this because it "just works" with a minimal target footprint (apart from text strings, the whole HTTP server is probably 1-2k) and works with any browser.
FWIW a Python interpreter (probably a similar thing to your JS interpreter) was examined, to run on the target, and it would have used maybe 50k of binary, plus 50k of RAM, plus a load of time interfacing it to the hardware features.
-
I also have a question, can you recommend a good hammer to fit the square block to the round hole?
(https://www.wooden-toys-selecta.com/wp-content/uploads/2020/07/62005-wooden-toy-storage-sorting-box.jpg)
-
I have no problems with visualizing. I have used Sciter.JS as a GUI and C++ for an algorithm for a long time. What I don't like is conversion between C data and JavaScript GUI output. There are good points provided in this topic. What I want to try is to make a minimalistic JavaScript HTML Canvas framework which is controlled from the microcontroller. Something very simplistic: controller sends commands to create a 2d canvas, and web browser creates it. The same with the pixels, graphs, numbers. But the development should be done on a more powerful MCU to eliminate flashing operation. (Running from RAM). Ultimate goal is to reduce number of used IDEs. Currently, I use notepad++ for HTML/JavaScript/Sciter.JS editing, codeblocks for C++ code, stm32cubeide for microcontroller programming. I think it can be automated in a way that everything can be done without leaving STM32CUBEIDE. Imagine a web browser window opened on a second monitor and automatically updating layout and content of various GUI elements depending on what is being sent from STM32's serial port through web serial api.
-
I have no problems with visualizing. I have used Sciter.JS as a GUI and C++ for an algorithm for a long time. What I don't like is conversion between C data and JavaScript GUI output.
But why not? JavaScript uses IEEE double precision FP values for everything, and C perfectly handles those. It's also very easy to read and write JSON from C.
I just don't understand the problem here.
It's also very easy to run C/C++ code in the web broswer if you want to, using emscripten. And it's quite frankly, stupid to run anything except C/C++ on microcontrollers.
-
I've done this on a 32F417 product I developed recently. All in GCC C. I don't know much about JS so I paid some guys on freelancer.com to product that, for peanuts. If you want I can send you the HTTP server source (PM me). It uses the Netconn API of LWIP, not sockets, but it should be trivial to change it to sockets. The web page is generated with C strings. Simple enough although there are some irritating gotchas because a browser can display only a properly formed web page, nothing less.
Thank you, peter-h, and for the info on Python. It's an interesting approach, something to consider.
I also have a question, can you recommend a good hammer to fit the square block to the round hole?
Haha, I see what you mean. But when you look it from practical perspective, I am 100% correct. Let's look at this great example:
STM32CubeMonitor, a "family of tools helps to fine-tune and diagnose STM32 applications at run-time by reading and visualizing their variables in real-time".
Ok, let's try to visualize a simple 1D array using this tool and the only way you will find is... my topic on how to achieve this:
https://community.st.com/t5/stm32cubemonitor-mcus/how-to-watch-an-array-using-stm32cubemonitor/td-p/225086/page/2
Seemingly right tool has no built-in solution to display a simple one-dimensional array. Btw, I've applied for a job at ST Microelectronics, but after "carefully reviewing my application" they decided that "other candidates better fit the requirements of the position". I need pretty basic functions, but there are none or they are poorly implemented.
Another example. Assume we need to find some bug which is related to a wrong DMA stride (we don't know yet). Is "debug" a right tool for this task? No. A obvious solution is to display memory contents as a 2d image. Highlight every byte that changes over time and you will spot the problem by noticing a dashed line of pixels in less than a second. After you fix the error, the dashed line will become a half-length whole line of pixels (changed stride from 32bit to 16bit).
-
But why not? JavaScript uses IEEE double precision FP values for everything, and C perfectly handles those. It's also very easy to read and write JSON from C.
Here is what's happening. Let's look into some of my old Sciter.JS developments:
function updFrame()
{
let someArr = [111,222,333];
let res = Window.this.assetInterface.readWave(someArr);
let colors = ["Black", "Red", "Green", "Blue", "Orange", "Gray", "Purple", "Lime"];
context[0].fillStyle="White";
context[0].fillRect(0,0,canvas[0].width, canvas[0].height);
for (let k = 0; k < 4; k++)
{
context[0].strokeStyle = colors[k];
context[0].beginPath();
context[0].moveTo(0,0);
for (let i = 0; i < GESTURE_FIFO_SIZE; i++)
{
let x1=canvas[0].width*i/GESTURE_FIFO_SIZE;
let y1=canvas[0].height-res[i*4+k];
if (i>0) {context[0].lineTo(x1,y1);} else {context[0].moveTo(x1,y1);}
}
context[0].stroke();
}
document.getElementById("testImage").requestPaint();
requestAnimationFrame(updFrame);
}
The above code is opened in Notepad++
It displays four curves that are coming from the following C++ code:
std::vector<int> readWave(const sciter::value vec)
{
// I can get some values sent from HTML (let someArr = [111,222,333]);
int32_t firstVal=vec[0].get<uint32_t> ( );
int32_t secondVal=vec[1].get<uint32_t> ( );
int32_t thirdVal=vec[2].get<uint32_t> ( );
// do something here
// read data from serial port
sendGet(&myPort,(uint8_t*)&dummy,2,GESTURE_FIFO_SIZE*4);
std::vector<int> tempArr (GESTURE_FIFO_SIZE*4);
for (int i=0; i<GESTURE_FIFO_SIZE*4; i++) // loop
{
tempArr[i]=(uint8_t) dummy[i];
}
// send data to HTML GUI
return tempArr;
}
The above code is opened in CodeBlocks.
The problems are:
1. Data changes daily. I can add array, remove some array, add some variable.
So, the first problem is that I need to maintain conversion code in C++ and in JavaScript simultaneously.
2. I don't like to press Ctrl+S/Ctrl+Shift+S in notepad++, switch to Codeblocks and press F9. It's so much slower that pressing F5 in web browser. Even if it's automated.
3. Sooner or later, I start to write some processing in JavaScript GUI code. While it's wrong, it's often faster this way. It comes very naturally. While I can force myself to keep writing everything in C, in many cases it is 5 minutes vs 1 minute or even worse. For example, making two new arrays in C++, maintaining the "transfer" to Javascript VS doing it Javascript if the data is already there. Therefore, at some point I have abandoned this CodeBlocks+SciterJS approach. I can get the working prototype much faster using a web-browser only approach. But at a cost: need to port it to embedded C later.
I did the Sciter and later SciterJS approach for 3+ years and can trace it on a Sciter forum. I don't think that it's lack of experience thing. In my opinion, it's a great tool for the final product GUI, but not for an iterative design.
About "right" tools. In context of STM32CubeMonitor adding a new array / variable means that you need to reload ELF file. It's not only clicking though. With lots of arrays and variables CubeMonitor freezes for 1+ minute before you can press the confirmation button.
-
I don't know how anything can take 1 minute or 5 minutes to compile and flash something for a microcontroller. My typical experience is 5 seconds or so. But I've never used CodeBlocks or STM32CubeMonitor. Maybe they're crap, idk.
Downloading python or javascript or other interpreted code to a microcontroller can reduce turnaround to a fraction of a second, but at a 100:1 or more cost in execution speed once it gets there. And then you're looking at a 100 MHz microcontroller vs a 5 GHz PC you're already fighting well uphill for execution speed on the microcontroller, even using C.
Furthermore, Javascript on a PC uses a JIT compiler that uses many MB -- maybe 100s of MB -- of RAM to produce fast native code for the PC. That capability is never going to fit on an STM32 which I think go up to a maximum of something like 640 KB of RAM.
At a guess, you'd be looking at a 10,000:1 performance difference between JavaScript on an STM32 vs JavaScript in your PC web browser.
Even Lua doesn't currently have a JIT running on microcontrollers.
It would be possible to make some on-device JIT for microcontrollers with a few hundred KB of RAM and a couple of MB of flash, but I don't know of anyone who has.
If you want sub-second downloading / updating of code on a microcontroller with somewhat near native code speed then I think Forth is probably your only option, unless you yourself turn out to be the first to write an on-chip JIT for JavaScript or Python or Lua or Scheme or similar.
You could also make something that compiled individual C functions on a PC and STM32 code in flash that received them, wrote them into RAM, and ran them from there. When I say "you" I mean someone, possibly not actually you.
-
To me it sounds like modifying your firmware project so that it compiles both for microcontroller and for PC, and adding libwebsockets to the PC compilation side, streaming data into web browser (so that you connect your browser to localhost) would actually be pretty close to what you want?
Javascript part would receive data (such as your 1D array) from firmware through websocket, plotting it on screen. And possibly have buttons / text fields to give commands to the firmware part.
By compiling the firmware on PC and running web server there, you don't have to think about flashing delays, resources, or networking on real hardware.
Then you can use the exact same firmware to do verification / integration testing, running longer test vectors (e.g., input as CSV files).
And the IDE problem is "doctor, it hurts when I do this" problem. If your problem is having multiple IDEs, simple solution, stop using them? I never use any and don't understand why would you do that.
-
I thought the OP was looking for a way to quickly modify the properties of a DSP algorithm running in an embedded target. Hence my suggestion of a primitive HTTP server, which accepts up, down, value entry, etc.
I even implemented a file upload (there is a FAT-FS 2MB FAT12 filespace) so one can update the whole firmware. With AES256, CRC32, etc, checks ;)
Yes there must be slick libraries for that sort of client-server functionality, but then you have to learn how to use them :)
-
I just don't understand the problem here.
Me neither. This reminds me of
"But I was thinking of a plan
To dye one's whiskers green,
And always use so large a fan
That it could not be seen."
Engineering is about simplicity. If you pile lots of things together and then work on negating the influence of these things on your work process you will waste lots of efforts and the solution will be very convoluted.
Instead, isolate your signal processing code into a self-sufficient module, make sure you can compile the code both on PC and on MCU. On PC, insert the code into an application which does visualizations. Once your code is working, all you need to do is re-compile the MCU project and run it on MCU. That's all there's to it.
If I was doing this, my task would compile the code on PC, process a training set of hundreds (or thousands) pre-recorded gesture files, calculate statistics to evaluate recognition accuracy, isolate bad cases and print debug info on them. With all this written in C, it would probably take a few seconds to compile and run through the whole set. Then, once the test set is working, run it through much bigger and independent database of gestures to see if it still works.
Testing on the chip is prohibitively time cosuming - producing even a 100 of gestures manually takes too much time. Using pre-recorded gestures is the only way.
-
Engineering is about simplicity. If you pile lots of things together and then work on negating the influence of these things on your work process you will waste lots of efforts and the solution will be very convoluted.
I agree about simplicity, and the risk of spending too much effort without much improvement is high. Let's not forget that I have a great working algorithms in JavaScript. MinGW C + external Sciter.JS GUI approach was irritating enough to try JS-only. To be honest, I even don't care about the time that it takes to create JS-only prototype (within reasonable limits). Because I feel great working on the prototype, interesting, there is a sense of progress and nothing stops the momentum. But later I experience the consequences of using JS instead of C.
Instead, isolate your signal processing code into a self-sufficient module, make sure you can compile the code both on PC and on MCU. On PC, insert the code into an application which does visualizations. Once your code is working, all you need to do is re-compile the MCU project and run it on MCU. That's all there's to it.
If I was doing this, my task would compile the code on PC, process a training set of hundreds (or thousands) pre-recorded gesture files, calculate statistics to evaluate recognition accuracy, isolate bad cases and print debug info on them. With all this written in C, it would probably take a few seconds to compile and run through the whole set. Then, once the test set is working, run it through much bigger and independent database of gestures to see if it still works.
Testing on the chip is prohibitively time cosuming - producing even a 100 of gestures manually takes too much time. Using pre-recorded gestures is the only way.
A great advice! A small remark is needed here. I am not working on gesture recognition. The product prototype that I was demonstrating at the 2023's competition used STM32H7 only to demonstrate possible applications, such as real-time audio pitch for DJ turntables, image scroll/zoom on the screen, etc. The recognition algorithm itself is fully analytical, doesn't use any neural networks or patterns, and can work on a sliding window without any memory by storing temporary data in few registers on ancient 16bit MCU. That was easy stuff! Made by me to meet the criteria of the competition to get funds for a greater project. And the selling attempt was basically to meet the criteria of "entrepreneurship". I am more like a researcher, but every entity I've reached to was redirecting me to this enterpreneurship competition. So, the video above is only to demonstrate how visualization works in the browser and the performance of final algorithm running on the device.
I'll try to rephrase. Imagine I do exactly as you say, and you can control each my step. So, I have written an "isolated signal processing code into a self-sufficient module" - or have I? This code changes all the time, and the difficulty is here:
1. I am not aware of any IDE/framework that will allow me to create a windows32 executable and create custom GUI outputs in a same convenient manner as in JavaScript+HTML. There are frequent changes, and I usually have a chain of algorithms with certain inputs/outputs that sometimes I want to visualize.
2. Dynamic typing in JavaScript helps a lot but complicates the porting on limited memory device. For example, copy-pasting of something like a=b-c to C from JavaScript will create a bug if c>b and both defined as unsigned numbers (uint8_t).
-
Let's not forget that I have a great working algorithms in JavaScript.
I think that if your greater plan is to have these run on various devices, including embedded (but possibly also desktop and servers), reimplement them in C and after that, keep maintaining them in C; C is probably one of the sanest choices between portability and efficiency and it seems you already have grasp of the language. Choosing right tool for the job is important, and maintaining everything in two languages is not a long term solution (and probably not even short term) as you have realized already.
1. I am not aware of any IDE/framework that will allow me to create a windows32 executable and create custom GUI outputs in a same convenient manner as in JavaScript+HTML. There are frequent changes, and I usually have a chain of algorithms with certain inputs/outputs that sometimes I want to visualize.
You need a graphics library or data visualization library or GUI library. If you have been googling IDEs and frameworks no wonder you won't find anything. You don't need IDE or framework; "frameworks" specifically are evil's tools to lock down choices and add complexity to any project.
There are so many visualization libraries available that you will struggle to choose; I have used SFML in past but it's no good for graphs etc. I'm sure you can get some recommendations here, or do a Google search.
2. Dynamic typing in JavaScript helps a lot but complicates the porting on limited memory device. For example, copy-pasting of something like a=b-c to C from JavaScript will create a bug if c>b and both defined as unsigned numbers (uint8_t).
Yeah. This is solved by switching the algorithm into one language only. If you don't feel like using graphics libraries from C or C++, JS can still be decent option for visualization; in that case you would want to output/input data from/to C using double FP data type, but keep the algorithm itself C only.
-
Imagine I do exactly as you say, and you can control each my step. So, I have written an "isolated signal processing code into a self-sufficient module" - or have I? This code changes all the time,
You're in the process of creating it (since the code is changing). You currently have 2 codes - JS and C, and you must change them both in sync. This is a nightmare, even if you find/write tools to automate this somehow. I propose you rewrite it all in C and maintain it in C. Your module will have well-defined inputs and outputs. Physically, this would be a number of .c and .h files. You can sym-link them into any number of project. The point is, whether you compile the firmware for an MCU (or for a number of MCUs) of for a test program on PC (or for lots of different test programs), you always use the same files - once a file changes, this change is visible everywhere.
1. I am not aware of any IDE/framework that will allow me to create a windows32 executable and create custom GUI outputs in a same convenient manner as in JavaScript+HTML. There are frequent changes, and I usually have a chain of algorithms with certain inputs/outputs that sometimes I want to visualize.
IDE doesn't create executables, compilers do. There are two very common on Windows - MSVC++ and GCC, both free. Create a bat file or a makefile which compiles the project for you (this is very short, may be even one line). A decent text editor will let you run the file, compile the executable and even launch it.
Windows has API (WinAPI) which is installed on every Windows. It includes GDI module, which is used for drawing. Although very primitive, it let you draw various objects. For example, there's an ellipse function, which draws a circle.
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-ellipse
To draw a black circle with a red circle inside, you will have to call this function twice. Doesn't look hard to me. And will be much faster from pressing button to looking at the results than any JS. Or, if you're scared of WinAPI, or need more features, there are various drawing frameworks you can use.
2. Dynamic typing in JavaScript helps a lot but complicates the porting on limited memory device. For example, copy-pasting of something like a=b-c to C from JavaScript will create a bug if c>b and both defined as unsigned numbers (uint8_t).
Looks like JS doesn't actually make your life easier. There are periods where it may make things easier (such as when you write), but then it requires porting to C. In my view, the drawbacks outweigh the benefits. So, don't use JS then. You will have to port the existing code to C (if you haven't already), but if you stop using JS afterwards, it'll prevent future complications.
-
Take a look at https://github.com/cesanta/elk
If you want an interpreter that works on Arduino Nano.
However, whilst tiny, it is slow and SEVERELY stripped-down.
-
you may try the project names Espruino, which utilizes ESP32 to implement javascript engine parsing js.
-
how about Espurino?
it is a project that implements a interpreter on some MCUs such as ESP32,ESP8266 and stm32 etc.
-
For similar processing (sound reactive RGB) I used C# and win forms.
Autogain, FFT, binning etc was done with simple functions using exactly uint32, 16, etc.. to be able to directly copy it to MCU.
result graphs was done on a canvas pixel by pixel.
It wasn't pretty but in a day I could fast experiment with code and adjust real-time the coefficients with sliders and view the result.
-
Obviously, any interpreter for a dynamically typed language with managed memory is going to be orders of magnitude slower than a language like C/C++ that's designed to be statically typed and analyzed (more so C++) at compile time, but on a fast modern processor it can still be fast enough. Just probably not for any sort of DSP applications, but it's not beyond the realm of possibility with a 400MHz CM7. However, this assumes it doesn't do things like garbage collect at the wrong time, and doesn't expect the interpreter to run out of RAM.
-
Have you tried to ask chatgpt to translate it for you? You can give it as many hint as you need, and then review the code and tweak.
May be a time saver.
-
Have you tried to ask chatgpt to translate it for you? You can give it as many hint as you need, and then review the code and tweak.
Instead, I ask it to find errors in my translation. It has spotted type casting and signed/unsigned problems several times.
Translation from C to JavaScript itself is very easy for me.
I do things like this:
// JavaScript:
maxIndHistory.splice(0,1); // shift
maxIndHistory.push(curSpectrumMaxInd); // store
// C:
memmove(&maxIndHistory[0],
&maxIndHistory[1],
sizeof(maxIndHistory)-sizeof(maxIndHistory[0])); // <--- shift
maxIndHistory[HISTORY_SIZE-1]=curSpectrumMaxInd; // <--- store
Problems often emerge from things like this:
// JavaScript arm_sqrt_q31() "simulation"
deviation=Math.round(Math.sqrt((deviation/2)/0x7FFFFFFF)*0x7FFFFFFF);
deviation=deviation/32768/16; // /32768 - convert to normal sqrt output /16 to scale into 0.255 range
// C:
deviation = deviation >> 1;
arm_sqrt_q31((q31_t)deviation,(q31_t *) &deviation);
deviation = deviation >> 19; // SHR 15+4 /32768 / 16
I need to maintain this stuff. Plus, I have a separate MATLAB-like script to calculate some coefficients. I'm in the process of eliminating this stuff. Will take some time, but it's worth it.
-
Here is proof of concept of my DIY data visualization.
STM32 C code: macros DBG() passes variable name, size and data over serial connection to HTML web page
HTML+Javascript: using Web Serial API to receive commands sent by STM32 and dynamically create canvases to display data.
I think it's already better than STM32CubeMonitor :-DD
I just put DBG_1D() for any array and instantly see how it updates in Realtime in my web browser.
It would be perfect if everything can be done using single DBG(), but I haven't figured how to distinguish between variables/1d/2d arrays using plain C macros. That's why there are three macros DBG, DBG_1D and DBG_2D. As you can see, I made DBG_MAX() to implement scaling. The same way graph colors may be directly controlled from STM32 application. Any suggestions how to make it better?
(https://www.eevblog.com/forum/microcontrollers/cheap-microcontroller-for-javascript-or-fast-interpreter/?action=dlattach;attach=2478503;image)
-
It's possible to do things pretty much like that with zero (or almost zero) code, using (say) Kst.
All you need to do is log your serial data (if your serial console software supports that). Kst will act on file changes and plot in real-time.
I've tried doing it in Python in the past too, and it's possible to send the configuration over serial too. I chose to send out JSON from the microcontroller (not really a serial streaming protocol! there will be better options), and then the Python code self-creates charts according to what's coming over the JSON-over-serial (say, writing the following over the UART: {“temperature”: 21.7, “sound”: 45} {“sound”: 47} {“sound”: 44} {“temperature”: 21.5, “sound”:43} and so on).
Screenshot example below in case it gives you ideas.
Sometimes it's easier to do something custom for the task-at-hand, for instance using WebSerial and JavaScript as you have done, although just a personal preference, I don't do anything except simple displaying of values etc with JavaScript, I'd rather do any data work with Python or Matlab etc.
-
Just for the completeness:
The Microvium JavaScript engine for microcontrollers takes less than 16 kB of ROM and 64 bytes of RAM per VM while idle, making it possibly the smallest JavaScript engine to date with more language features than engines 4x its size.
I haven't tried it, but I liked the idea:
A foundational principle in Microvium is the ability to the snapshot the state of the virtual machine so that it can be restored and resumed later in another environment.
https://coder-mike.com/blog/2022/06/11/microvium-is-very-small/
https://microvium.com/getting-started/
-
I'm surprised OP responds to his own tread half a year later...
I'm with bson. Develop the algorithms in C / C++ and run, test and debug them on PC hardware. Only in the last stages of the design, run them on a uC.
C++ has evolved a lot in the last 10+ years. New constructs, better optimizations, more modern methods for code design. from "auto" to constexpr and other stuff. I you have not closely at C++ lately, you really have to put in some effort to get up to speed, both with learning the new stuff, and also for shedding the old crud and habits. I'm guessing that half of C++ simply never should be used. It's only there because legacy code must be supported.
Also, with compartmentalizing your code in separate files, and paralleling the make process (with a -j12 or -j96 if you have that many cores on your PC) a build can be quite quick. (of course still depending on code size and such).
-
I'm guessing that half of C++ simply never should be used. It's only there because legacy code must be supported.
Probably. Unfortunately different people choose different subsets.
Having to be backward compatible limits the opportunities for clean language design and hence clean code design. Sometimes, as with COBOL, it is better to start afresh wherever possible.
It is quite entertaining/sad that people often say you shouldn't teach students old processors, but should use modern processors such as ARMs - and in the next breath insist that an old language should be used. Oh well.
-
I'm surprised OP responds to his own tread half a year later...
with a useful idea of transferring VM state. I never thought about it this way, and it may spark some more ideas
-
But why not? JavaScript uses IEEE double precision FP values for everything, and C perfectly handles those. It's also very easy to read and write JSON from C.
I just don't understand the problem here.
Just try translating this simple C code to JavaScript and you'll quickly see the challenges of float64 precision in JS. It should produce exactly the same result.
double v = sqrt(2);
printf("%.17g\n", v);
:popcorn:
Another major issue in JavaScript is its handling of integers. I once built a calculator that needed to support int32, uint32, int64, and uint64 types, and I initially underestimated how problematic this would be. JavaScript’s Number type can’t accurately represent 64-bit integers, and even 32-bit operations can behave unexpectedly. In the end, I had to abandon standard numeric types and switch to BigInt for arbitrary-precision arithmetic - but while it solves the accuracy problem, it's far less convenient to work with.
That experience really made me appreciate how convenient working with int and float types is in C. You don't usually notice it in day-to-day coding, but C’s strong and explicit type system makes handling numeric data much more easy, predictable and manageable - especially compared to JavaScript, where the lack of strict typing and uniform Number type leads to subtle bugs and kludges.
-
But why not? JavaScript uses IEEE double precision FP values for everything, and C perfectly handles those. It's also very easy to read and write JSON from C.
I just don't understand the problem here.
Just try translating this simple C code to JavaScript and you'll quickly see the challenges of float64 precision in JS. It should produce exactly the same result.
double v = sqrt(2);
printf("%.17g\n", v);
:popcorn:
Not sure what you're trying to get at here.
Mac-mini:programs bruce$ cat foo.c
#include <stdio.h>
#include <math.h>
int main(){
printf("Hello World! %.17g\n", sqrt(2));
return 0;
}
Mac-mini:programs bruce$ gcc -O foo.c -o foo
Mac-mini:programs bruce$ ./foo
Hello World! 1.4142135623730951
Mac-mini:programs bruce$ python3
Python 3.13.3 (main, Apr 8 2025, 13:54:08) [Clang 16.0.0 (clang-1600.0.26.6)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 2**(1/2)
1.4142135623730951
>>>
Mac-mini:programs bruce$ node
Welcome to Node.js v23.11.0.
Type ".help" for more information.
> 2**0.5
1.4142135623730951
>
Exactly the same from C, Python, and JavaScript. I don't see how it could be otherwise.
Another major issue in JavaScript is its handling of integers. I once built a calculator that needed to support int32, uint32, int64, and uint64 types, and I initially underestimated how problematic this would be. JavaScript’s Number type can’t accurately represent 64-bit integers
Correct. JavaScript stores all numbers as double, which exactly represents and calculates with all integers up to and including ±2^53.
and even 32-bit operations can behave unexpectedly.
Like what? 32 bit integer operations are exact in double, you just have to make sure to mod values by 2^32 from time to time, or truncate fractional values if you're doing division.
Convincing a JavaScript JIT that your values are integers and to use your CPU's integer instructions instead of floating point is of course a lot of fun, but that's a speed optimisation not a correctness thing.
-
Exactly the same from C, Python, and JavaScript. I don't see how it could be otherwise.
This is actually not what I asked for, I asked for formatting with specific precision. Your code for js doesn't have precision specifier.
Well, try to do the same for let say "%.10g" and "%.20g" :)
And then try to translate this one:
printf("%+.4f\n", 15.0/123.0);
-
Like what? 32 bit integer operations are exact in double, you just have to make sure to mod values by 2^32 from time to time, or truncate fractional values if you're doing division.
Convincing a JavaScript JIT that your values are integers and to use your CPU's integer instructions instead of floating point is of course a lot of fun, but that's a speed optimisation not a correctness thing.
significant challenges arise once you start dealing with signed integers and need to accurately emulate overflow behavior, sign extension, and wrapping semantics to precisely match the behavior of native int32 or uint32 types. Ensuring this correctness in JavaScript requires extra care and often manual handling, since the language’s numeric model doesn’t natively enforce these integer semantics. So while JIT optimizations for integer operations improve performance, the real complexity lies in faithfully reproducing the exact integer behaviors, especially around overflows and sign handling.
In other words, when you simply need to perform basic calculations, JavaScript handles numbers reasonably well. However, once you impose strict requirements for precise handling across the full range of values and all possible operations, significant challenges arise. These issues tend to surface repeatedly and unexpectedly - much like a swarm of cockroaches emerging from a closet, making reliable, high-precision numeric programming in JavaScript quite difficult.
There is also a significant lack of native support for error-free calculations with 64-bit integer values which is usually present in C.
-
> Like what? 32 bit integer operations are exact in double, you just have to make sure to mod values by 2^32 from time to time, or truncate fractional values if you're doing division.
If what you care is correctness, you can abstract it with JS classes, int32, int64, etc.
https://chatgpt.com/share/68412f51-f62c-8000-a6b3-f71e3bf279bb
-
I'm guessing that half of C++ simply never should be used. It's only there because legacy code must be supported.
A lot of C++ seems to be added at being able to make the STL very ... flexible.
Looking at STL source code is almost like looking at another language.
-
I'm guessing that half of C++ simply never should be used. It's only there because legacy code must be supported.
A lot of C++ seems to be added at being able to make the STL very ... flexible.
Looking at STL source code is almost like looking at another language.
What's debugging like?
Bear in mind that the STL is a Turing-complete language "hidden" inside C++. Some valid conforming C++ programs can never complete compilation - because they cause the compiler to emit the sequence of prime numbers during compilation!
"Hidden" is stunningly accurate: the STL language creators refused to believe they had created a Turing-complete language, until Erwin Unruh rubbed their noses in it[1]! If the language designers can't understand their own language, what hope do mere mortals have? Is that a sound basis for an engineering endeavour?
[1] https://en.wikibooks.org/wiki/C%2B%2B_Programming/Templates/Template_Meta-Programming#History_of_TMP
-
I'm guessing that half of C++ simply never should be used. It's only there because legacy code must be supported.
A lot of C++ seems to be added at being able to make the STL very ... flexible.
Looking at STL source code is almost like looking at another language.
What's debugging like?
Bear in mind that the STL is a Turing-complete language "hidden" inside C++
You are confusing a language feature, templates, and a library, STL, developed by Stepanov at HP (and published as open source software by HP in 1994) that makes use of templates (and other) language features.
STL hasn't existed since the C++98 standard when equivalent functionality was absorbed into the C++ standard library as "standard containers" and "standard algorithms".
-
I'm guessing that half of C++ simply never should be used. It's only there because legacy code must be supported.
A lot of C++ seems to be added at being able to make the STL very ... flexible.
Looking at STL source code is almost like looking at another language.
What's debugging like?
Bear in mind that the STL is a Turing-complete language "hidden" inside C++
You are confusing a language feature, templates, and a library, STL, developed by Stepanov at HP (and published as open source software by HP in 1994) that makes use of templates (and other) language features.
STL hasn't existed since the C++98 standard when equivalent functionality was absorbed into the C++ standard library as "standard containers" and "standard algorithms".
Whoops!
I gave up on using C/C++ professionally just before that, partly because of the template mess, partly because of the "cast away constness" irreconcilable dilemma, and partly because for non-embedded applications there were other better (fast, productive, expressive) languages.
-
If what you care is correctness, you can abstract it with JS classes, int32, int64, etc.
Yes, it's true that you can implement abstractions like int32, int64, uint32 and uint64, and others using JavaScript tools to emulate correct numeric behavior. For example I used BigInt to bypass these js issues, it is much slower and more complex to handle, but it give you predictable behavior. However, this is not what most developers expect from a programming language when implementing algorithms. The goal is to focus on the logic itself - not to spend time fighting around language limitations and kludges, dealing with edge cases and building virtual machines just to get reliable number handling. A language should provide robust native support for such fundamental types, especially when correctness and predictability are critical.
JavaScript is quite convenient for quickly building utilities with a graphical interface that run on any machine with a web browser. However, as a programming language, it is just a piece of shit, particularly when it comes to code analysis and implementing logic that requires precise numerical computations. Its dynamic typing and loosely defined numeric behavior often make it more of an obstacle than a tool in such contexts.
However, there are currently no real alternatives that offer the same ease and portability for writing browser-based applications, so we often have to work within the limitations of what's available. This is precisely why tools like Emscripten have emerged - essentially implementing a virtual machine in JavaScript that allows code written in more robust, strongly typed languages to run in the browser while leveraging the GUI capabilities available through JavaScript.
Returning to the topic of using JavaScript on microcontrollers - it's far from an ideal choice. MCU environments lack web-browsers, and the language itself is highly resource-intensive, both in terms of runtime and memory footprint. Given the limited resources available on typical microcontrollers, JavaScript is generally unsuitable for such low-level, performance-constrained applications.
As for the challenges of porting code from other languages to JavaScript, one of the most notable issues is the lack of proper numeric formatting support. What is straightforward and reliable in other languages often becomes a source of frustration in JavaScript, requiring workarounds, kludges and unreliable hacks to achieve the same level of precision and control.
-
> However, as a programming language, it is just a piece of shit
Crap languages rule the world. Take Python for example. If JavaScript would support operator overloading, using the numeric abstract classes would be more straight forward, allowing statements such as a = b + c.
BTW, Python is available also for microcontrollers https://en.wikipedia.org/wiki/MicroPython
-
I have once experimented a bit with MicroPython. I flashed it on a Black Pill (STM32F411) after soldering on an external flash chip. After that, when plugged in to your PC via USB it acts as an external drive. I think I loaded a python script directly from that thing in a text editor, and it automatically executes the script when it's saved. But I'm not sure. It also had options to set a sort of batch file, that runs at power up.
I found the whole thing a bit intriguing, but I don't really know what to do with microPython on a microcontroller. I rather use C / C++ myself. One thing for which micropython may be interesting is to see the results of direct register manipulation, and to learn how the STM32 works from that.Via the REPL you can read from and write to registers, and there is some way to get symbolic register names, so you don't have to look up the binary addresses yourself. But the syntax is quite weird. MicroPython has also versions for a bunch of other platforms.
On a sidenote:
How good is AI these days to translate sourcecode form one computer language to another? It probably is not perfect, but I guess it will save you some time if it works reasonably well.
-
On a sidenote:
How good is AI these days to translate sourcecode form one computer language to another? It probably is not perfect, but I guess it will save you some time if it works reasonably well.
If the result doesn't have to work properly (i.e. only reasonably well), then I can generate it very quickly without LLMs.
-
Hmm. What processor architectural features lead to faster interpreter or emulator performance?
-
Hmm. What processor architectural features lead to faster interpreter or emulator performance?
Not Jazelle, as it turned out :-)
-
Hmm. What processor architectural features lead to faster interpreter or emulator performance?
It should probably look as much as possible like either amd64 or aarch64. That is, a platform with a JIT compiler tool chain with billions of dollars of development effort behind it.
-
Hmm. What processor architectural features lead to faster interpreter or emulator performance?
It should probably look as much as possible like either amd64 or aarch64. That is, a platform with a JIT compiler tool chain with billions of dollars of development effort behind it.
JIT is the starting point, but significantly more performance is possible if you can add HotSpot style performance improvements.
NRE charges dominate :)
-
a platform with a JIT compiler tool chain with billions of dollars of development effort behind it.
That is not an "architecture feature." That's just capitalizing on "prior art"!
I want to know which CPU features make it easy to write better interpreters WITHOUT that much development effort.
(Sort of like "x86 chips prove that CISC is as good or better than RISC, if only because AMD and Intel put so much effort into making it so." (observation is from about when Apple switched to x86 from PPC, and the MIPS desktop vendors went away. Probably less valid now...))
-
If what you care is correctness, you can abstract it with JS classes, int32, int64, etc.
However, there are currently no real alternatives that offer the same ease and portability for writing browser-based applications, so we often have to work within the limitations of what's available. This is precisely why tools like Emscripten have emerged - essentially implementing a virtual machine in JavaScript that allows code written in more robust, strongly typed languages to run in the browser while leveraging the GUI capabilities available through JavaScript.
Yes, or under the hood, basically WebASM.
Its basically a custom ISA that can be run in a web browser (which yes, would have to be loaded via javascript).
Here is the list of all opcodes: https://webassembly.github.io/spec/core/appendix/index-instructions.html
You can even spin up Wasm docker containers nowadays. I think you'll have to provide it some modules/interfaces on how it can talk to the outside world, but many server backends can do a ton once it can open TCP connections and/or HTTP+SQL.
I'm sure fancy desktop Wasm runtimes will JIT your code and make it run real fast. But here are also two libraries I found that do embedded (ESP32/Cortex-m7 level): https://github.com/bytecodealliance/wasm-micro-runtime
https://github.com/wasm3/wasm3
It does use a ton of RAM though.
But, I think for most applications this is still thinking backwards. Wasm allows you to write C/C++/LLVM code and run that in a webbrowser. This bypasses all the JS type bs while still leveraging browser environments for quick results. Then if you want to test test/visualize code, you could also use the browser with Wasm module that contains code under test. When debugging is done, you integrate it into your MCU project. There would be zero need for an embedded scripting engine, zero need for code porting, as <stdint.h> should be portable.
-
a platform with a JIT compiler tool chain with billions of dollars of development effort behind it.
That is not an "architecture feature." That's just capitalizing on "prior art"!
I want to know which CPU features make it easy to write better interpreters WITHOUT that much development effort.
I don't think there is a shortcut. Dynamic recompilation with hotspot analysis is the way to get good performance out of an interpreted language. It's not that architecture doesn't matter at all, just that as long as you have something reasonably sane and generally high performance you are going to be able to write the code generator for it.
Trying to have the processor directly execute the interprete code, as with Bruces' comment about jazelle, is the wrong way to do it. Maybe there are techniques that would really help, but mostly JIT compilers are compilers. Most of the things that make compiled programs fast make JITs fast.
(Sort of like "x86 chips prove that CISC is as good or better than RISC, if only because AMD and Intel put so much effort into making it so." (observation is from about when Apple switched to x86 from PPC, and the MIPS desktop vendors went away. Probably less valid now...))
No that shows that when you reach a certain scale instruction decoding becomes a progressively smaller fraction of your processor. There are still costs associated with e.g., tracking micro-ops through the pipeline so you can handle exceptions properly but once you have a super scalar processor with deep reorder buffers, register renaming, and multiple execution units, speculative execution, branch prediction and other internal caches, and so on that's where most of the effort goes. RISC is clearly superior, but that doesn't save you all the hard work of making an actually high performance processor, at least not since the 90s.
-
On a sidenote:
How good is AI these days to translate sourcecode form one computer language to another? It probably is not perfect, but I guess it will save you some time if it works reasonably well.
If the result doesn't have to work properly (i.e. only reasonably well), then I can generate it very quickly without LLMs.
The LLM thing can type a lot faster then you can. You'd still have to comb through the code and debug it, but you can skip the boring parts such as changing syntax for how a for loop looks like in that other language, sorting out where and how you declare variables and such.
-
On a sidenote:
How good is AI these days to translate sourcecode form one computer language to another? It probably is not perfect, but I guess it will save you some time if it works reasonably well.
If the result doesn't have to work properly (i.e. only reasonably well), then I can generate it very quickly without LLMs.
The LLM thing can type a lot faster then you can. You'd still have to comb through the code and debug it, but you can skip the boring parts such as changing syntax for how a for loop looks like in that other language, sorting out where and how you declare variables and such.
You are missing the point, in several ways.
-
Javascript is not a "thing". It doesn't exist. It's a "type" of thing. There is no standard.
You can try for ECMA or TypeScript which have tighter standards, but overall "Javascript" is highly dependant on what your sandbox environment is and how exactly you choose to implement the language.
There are vast and total incompatibilities between the most common browsers. Code for A will not run on B. There are fundamental differents which are not code compatible. Frame works like Angular/React et. al. include huge sections for cross compatible lower layers that are switched on a browser by browser basis.
If these didn't exist, NOBODY would be using Javascript in industry as it's a total waste of time to try and write it and expect it to run without doing your own testing across all combinations of browser and OS.
Best avoided.
Note. On a modern "Windows" or "Linux" PC, javascript is NOT an interrupted script language. Hasn't been for a decade or more. It is a natively compiled and directly executed language. Chrome, Edge etc. were subjected to the Spectre CPU cache exploit for example and you could use the exploit vector from JS.
Linux folks will have noticed Chrome and others shifting to a "jail" or "sandbox" model. This is why. When you visit that dodgy website their JS code is compiled and executed directly on your CPU, NOT in a VM. So the browser itself is locked down to try and keep things sane and secure.
Do not expect the kind of speed you see on a modern desktop browser if you try and use JS on a lesser system without the native compilation. Orders of magnitude slower.
-
There are vast and total incompatibilities between the most common browsers. Code for A will not run on B. There are fundamental differents which are not code compatible. Frame works like Angular/React et. al. include huge sections for cross compatible lower layers that are switched on a browser by browser basis.
It is much better now than it was 20 years ago. Of course, there are differences, but the level of compatibility in Javascript and HTML implementation is stunning. Even the browsers are running in phones. This is one of the things that really improved over the years. Yet, the bran-dead Web developers (aka Full Stack specialists) manage to write code which works in some browsers and fail in others.
If these didn't exist, NOBODY would be using Javascript in industry as it's a total waste of time to try and write it and expect it to run without doing your own testing across all combinations of browser and OS.
Testing is mandatory anyway - you're not going to put untested code onto your site, would you? By using a subset of JavaScript/DHTML which is supported the same everywhere, you can do practically anything.
The JavaScript frameworks actually make compatibility issues worse. You may not have changed anything in your code, but the framework might have changed, or dropped support for older browser, or did something else of that sort, so now your code doesn't work, and you don't even know that. Your customers cannot watch your website any more, but this is only because your customers are bad. The bastards should've upgraded to Windows 11 with latest Chrome.