|
15442
|
Cornilieus by @Falling10fruit | Stardance
|
Falling10fruit
|
0
|
126.33
|
0
|
[]
|
[{"post_id": 80316, "ts": "202 [{"post_id": 80316, "ts": "2026-10-17T08:24:29Z", "hours": 27.3931, "text": "Ah a bit of work here and there amidst A levels. what a shame for those 17 hours. whoops :) made another vite folder within the repositories so I can test out shaders without the other parts of the project getting in the way."}, {"post_id": 48774, "ts": "2026-08-17T09:44:53Z", "hours": 1.9389, "text": "So remember when last time I thought “oh no how to do private prefix arrays”? Well, an hour later and I discovered “Oh, my 2^24 entities can be divided by 256 and then 256 again so I’ll just have to do 3 prefix sums layered one on top of each other on the thread level” I don’t know what happened but my brand new 8 bit radix pass takes 460+ms. Yay"}, {"post_id": 48316, "ts": "2026-08-16T16:59:36Z", "hours": 7.4489, "text": "So I’ve hit a brick wall. gogle told me that to improve my 4 bit radix shader is to do 8 bits at a time to halve the number of passes (and that a byte is industry standard) but I can’t figure out how to like create a private histogram for each of the 256 digits also I got a 72 on math AS levels :P"}, {"post_id": 41934, "ts": "2026-08-06T13:43:28Z", "hours": 15.7667, "text": "Hey guys so I reduced the chunk size so that joints don’t have to hope that there’s at most 16 entities within 16 tiles Also I was reading the dev time of my other project and I accrued almost 6 hours over the limit, whoops :P"}, {"post_id": 31103, "ts": "2026-07-19T07:12:44Z", "hours": 1.75, "text": "I made a pool of unmapped buffers so I can continuously record the time taken so I can clearly see how much worse my changes to the algorithm are."}, {"post_id": 30643, "ts": "2026-07-18T11:47:17Z", "hours": 1.6572, "text": "I tried to optimize sorting by reducing the memory footprint of every thread so they could fit more threads. Mid-bitpacking, I decided to test the pipeline and viola. I’ve doubled execution time. Also rerolls only add like 10% or less of what you initially rolled. Absolute scams"}, {"post_id": 30186, "ts": "2026-07-17T15:37:17Z", "hours": 1.0831, "text": "So by bitpacking I was able to make the array longer while fitting within the shared memory limit. That means that more threads can work on the kogge stone -> larger workgroup size -> less iterations per thread. school’s back btw"}, {"post_id": 29380, "ts": "2026-07-16T09:26:18Z", "hours": 7.0303, "text": "So the sorting system is taking a bit too long, ah well I’ll figure it out. Google says it’s possible to get it fast enough"}, {"post_id": 26708, "ts": "2026-07-11T17:33:06Z", "hours": 8.0044, "text": "So I was trying to visualize the buffer on a helper static website so I can eyedrop data since reading a 16384 length array isn’t efficient but apparently chromium dropped an update that prevents mouse interactions the moment you use their builtin eyedropper so here’s my replacement (not what all 8 hours was spent on pls believe me)"}, {"post_id": 25985, "ts": "2026-07-10T15:13:37Z", "hours": 13.3789, "text": "So I forgot to add rotations to the collision thing. Also I got my first ever Device Lost error yay. No one told me that not posting for over 10 hours will deduct me of my precious work."}, {"post_id": 24278, "ts": "2026-07-07T18:55:34Z", "hours": 8.4122, "text": "some stufff so collisions taking their sweet time with gjk are seperated and moved to the entities_buffer_meta so the gpu can focus on resolving those stubborn collisions look, after several parallel friendly paradigms the last step of my code is this sequential and divergent code"}, {"post_id": 22349, "ts": "2026-07-04T19:39:15Z", "hours": 8.0772, "text": "Man I made a lot of mistakes earlier whoop. Anyways here’s a naive implementation of the support function for the gjk algo. I’m going to move the entity_nodes buffer into shared memory (or at least the collision pair’s colliding mesh nodes into private memory) tomorrow"}, {"post_id": 18412, "ts": "2026-06-29T15:34:11Z", "hours": 1.4953, "text": "Yay yesterday’s issue is fixed, in theory. Man, I haven’t actually like tested any of this code. Should I just print the data from the buffer and eye it or actually try to render it… Cause I’m definitely am going to spend a while getting the render to work in the first place."}, {"post_id": 17822, "ts": "2026-06-28T19:12:22Z", "hours": 2.5992, "text": "I’m almost done with the algo that finds the memory offsets to fit colliding data but uh, I just realized I don’t have space anymore :) I think I’m going to revert last update’s progress. fun"}, {"post_id": 17120, "ts": "2026-06-27T20:03:53Z", "hours": 4.5011, "text": "After doodling a bit, I decided that the collision handling should deal with 8 collision pairs at a time. I forgot how to implement kogge stone which we will need and oh boy do I not know where to put the gjk phase. I’m already taking up 0.8 GiB vram at best. Also, here’s a histogram with nit picking bit packing."}, {"post_id": 13503, "ts": "2026-06-22T03:01:42Z", "hours": 1.6019, "text": "Shuffled buffers around and created a cos sin look up table since entities only have 2^13 possible rotations. Although I am using a 13-bit floating point for rotation velocity but it’s fine. That’s future me’s specifications to implement"}, {"post_id": 10483, "ts": "2026-06-17T07:07:50Z", "hours": 5.5903, "text": "So apparently changing the href attribute of a link means that the website has to request for the css file and wait while the component is loaded instantly causing FOUC So I just copy pasted every css file into the header so the stylesheets are always loaded. And to retain the ability to just id an element as “sidebar” without worrying about conflicts I just wrapped every stylesheet with the id of the main element. I was also optimizing bit unpacking and casting position and restructuring layouts. I swear I didn’t spend all 5 hours just writing 20 lines of code."}, {"post_id": 8293, "ts": "2026-06-13T19:45:11Z", "hours": 0.4575, "text": "Simple readjustment of the velocity precision. Previously the smallest value of the 10-bit float, we’re not going to implement subnormal values, accumulates to shift entities when idle."}, {"post_id": 8229, "ts": "2026-06-13T18:03:46Z", "hours": 1.5025, "text": "Wow I spent eternity making 10 boxes. And still tsx doesn’t like that I imported a png file, I should Google that up one day."}, {"post_id": 7617, "ts": "2026-06-12T18:52:34Z", "hours": 1.9397, "text": "Menu expands with little code. I’m going to run out of functional UI to work on and eventually will have to either fix the aesthetics or make the physics engine."}, {"post_id": 6932, "ts": "2026-06-11T16:52:13Z", "hours": 4.7031, "text": "I’ve been avoiding debugging WGSL learning Solid JS. Incredible UI design I know thank you :)"}]...
|
Title: Cornilieus by @Falling10fruit | Stardance
Title: Cornilieus by @Falling10fruit | Stardance
Devlogs:
I’ve been avoiding debugging WGSL learning Solid JS. Incredible UI design I know thank you :)
Menu expands with little code. I’m going to run out of functional UI to work on and eventually will have to either fix the aesthetics or make the physics engine.
Wow I spent eternity making 10 boxes. And still tsx doesn’t like that I imported a png file, I should Google that up one day.
Simple readjustment of the velocity precision. Previously the smallest value of the 10-bit float, we’re not going to implement subnormal values, accumulates to shift entities when idle.
So apparently changing the href attribute of a link means that the website has to request for the css file and wait while the component is loaded instantly causing FOUC So I just copy pasted every css file into the header so the stylesheets are always loaded. And to retain the ability to just id an element as “sidebar” without worrying about conflicts I just wrapped every stylesheet with the id of the main element. I was also optimizing bit unpacking and casting position and restructuring layouts. I swear I didn’t spend all 5 hours just writing 20 lines of code.
Shuffled buffers around and created a cos sin look up table since entities only have 2^13 possible rotations. Although I am using a 13-bit floating point for rotation velocity but it’s fine. That’s future me’s specifications to implement
After doodling a bit, I decided that the collision handling should deal with 8 collision pairs at a time. I forgot how to implement kogge stone which we will need and oh boy do I not know where to put the gjk phase. I’m already taking up 0.8 GiB vram at best. Also, here’s a histogram with nit picking bit packing.
I’m almost done with the algo that finds the memory offsets to fit colliding data but uh, I just realized I don’t have space anymore :) I think I’m going to revert last update’s progress. fun
Yay yesterday’s issue is fixed, in theory. Man, I haven’t actually like tested any of this code. Should I just print the data from the buffer and eye it or actually try to render it… Cause I’m definitely am going to spend a while getting the render to work in the first place.
Man I made a lot of mistakes earlier whoop. Anyways here’s a naive implementation of the support function for the gjk algo. I’m going to move the entity_nodes buffer into shared memory (or at least the collision pair’s colliding mesh nodes into private memory) tomorrow
some stufff so collisions taking their sweet time with gjk are seperated and moved to the entities_buffer_meta so the gpu can focus on resolving those stubborn collisions look, after several parallel friendly paradigms the last step of my code is this sequential and divergent code
So I forgot to add rotations to the collision thing. Also I got my first ever Device Lost error yay. No one told me that not posting for over 10 hours will deduct me of my precious work.
So I was trying to visualize the buffer on a helper static website so I can eyedrop data since reading a 16384 length array isn’t efficient but apparently chromium dropped an update that prevents mouse interactions the moment you use their builtin eyedropper so here’s my replacement (not what all 8 hours was spent on pls believe me)
So the sorting system is taking a bit too long, ah well I’ll figure it out. Google says it’s possible to get it fast enough
So by bitpacking I was able to make the array longer while fitting within the shared memory limit. That means that more threads can work on the kogge stone -> larger workgroup size -> less iterations per thread. school’s back btw
I tried to optimize sorting by reducing the memory footprint of every thread so they could fit more threads. Mid-bitpacking, I decided to test the pipeline and viola. I’ve doubled execution time. Also rerolls only add like 10% or less of what you initially rolled. Absolute scams
I made a pool of unmapped buffers so I can continuously record the time taken so I can clearly see how much worse my changes to the algorithm are.
Hey guys so I reduced the chunk size so that joints don’t have to hope that there’s at most 16 entities within 16 tiles Also I was reading the dev time of my other project and I accrued almost 6 hours over the limit, whoops :P
So I’ve hit a brick wall. gogle told me that to improve my 4 bit radix shader is to do 8 bits at a time to halve the number of passes (and that a byte is industry standard) but I can’t figure out how to like create a private histogram for each of the 256 digits also I got a 72 on math AS levels :P
So remember when last time I thought “oh no how to do private prefix arrays”? Well, an hour later and I discovered “Oh, my 2^24 entities can be divided by 256 and then 256 again so I’ll just have to do 3 prefix sums layered one on top of each other on the thread level” I don’t know what happened but my brand new 8 bit radix pass takes 460+ms. Yay
Ah a bit of work here and there amidst A levels. what a shame for those 17 hours. whoops :) made another vite folder within the repositories so I can test out shaders without the other parts of the project getting in the way....
|
1792684346.9161808
|
1793289146.9161808
|
|
16683
|
Blur Stuff by @AbdurRehman | Stardance
|
AbdurRehman
|
1
|
126.53
|
826
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-08-15T15:46:55Z", "hours": 118.0, "multiplier": 14.02, "payout": 826, "blessing": "neutral", "text": "I made Blur Stuff, a privacy focused tool for safely sharing images, videos, GIFs, and documents. It automatically detects sensitive information such as faces, license plates, text, and PII, then lets you anonymize the detected areas with different anonymization effects. It also removes EXIF metadata so other sensitive information isn’t left hidden inside the file. The goal of this project is to make sharing media safely as easy as uploading it. Instead of manually searching through a file like a document or photo and manually covering stuff you don’t want others to see, Blur Stuff does this work for you. The hardest part of making this project was making anonymization work reliably across different types of media. Images were relatively simple, but videos and GIFs require frame by frame processing while keeping detections consistent over time. And to make things efficient and fast I also had to deal with things like tracking which were really tricky. Connecting the frontend and backend was also another challenge and then hosting everything also brought it’s problems with dependency conflicts which took quite a while to solve. The thing I am most proud of is that this became a real working and professional looking application rather than just a prototype type thing. It supports most of the commonly used media formats, has multiple customization options among other neat functionalities. Also I like how this project is privacy focused. Like it doesn’t store the user’s files ever. It just processes them, removing EXIF, and gives the result. Blur Stuff can be tested by uploading an image containing faces, text, or a license plate and let it detect the sensitive regions and then select what you want anonymized, and run the anonymization. The current deployment has limited memory and resources since I’m on a free plan, so larger videos and documents can take a while to process, and very large files may run into resource limitations. The app is also deployed using Railway’s serverless option, so the server may need a few seconds to start up if it hasn’t received a request recently. Also the selected region anonymization for videos is still a bit buggy but mostly it works. These are areas I will improve in the future.", "repo": "https://github.com/RealAbdurRehman/blur-stuff", "demo": "https://blurstuff.up.railway.app"}]...
|
[{"post_id": 50269, "ts": "202 [{"post_id": 50269, "ts": "2026-08-19T13:00:35Z", "hours": 8.5269, "text": "Since shipping Blur Stuff, I got feedback that the frontend felt too generic so I completely revamped the UI to give it a more intentional and fun style. I redesigned everything from colors to typography and everything else. Now the app has a more personal feel. I also cleaned up some small things here and there."}, {"post_id": 47548, "ts": "2026-08-15T14:59:59Z", "hours": 6.7497, "text": "Since the previous dev log, I have been focused on getting Blur Stuff ready for deployment. This ended up being much more painful than I expected. The biggest part was dealing with dependency issues. The project worked locally, but moving everything to a production environment exposed a bunch of problems with Python versions, PaddlePaddle, PaddleOCR, PaddleX, PyYAML, spaCy, OpenCV and other dependencies. I spent a lot of time testing different configurations, fixing dependency conflicts, debugging the production container, and figuring out why things that seemed to work okay locally were breaking during deployment. The biggest headache was PaddleOCR. I had to deal with missing system libraries, model downloading, Paddle initialization issues and eventually a oneDNN runtime error that was causing OCR requests to return 503s. After a lot of debugging and testing different configurations, I eventually got the OCR pipeline working properly in production. That was probably the one of the most frustrating parts of the entire project, but it is finally done. I also switched the spaCy model from en_core_web_lg to en_core_web_sm. The larger model was unnecessarily heavy for what Blur Stuff needs and was making deployments take way longer. Switching to the smaller model made deployment faster and also improved performance without having a noticeable impact on the PII detection quality. After successful deployment I finished the README and added the final assets, screenshots, logo and documentation. I added the MIT license, favicon, deployment related changes and generally cleaned up the entire repository. At this point, I think Blur Stuff is ready to ship. There are definitely still improvements I could make, especially improving performance, making video anonymization more reliable and efficient, and polishing various parts of the application but I don’t want to spend more time trying to make everything perfect. The current deployment is also running on Railway’s free plan, so there are some resource and memory limitations since Blur Stuff runs several machine learning models in the same process. For now, I’m okay with that rather than spending more time optimizing around the limitations of a free deployment. For now, I’m calling it finished and shipping it since the core functionality is working and the project is deployed. I’ll definitely come back to Blur Stuff later to improve it, but I think it’s time to move on to something new."}, {"post_id": 46326, "ts": "2026-08-13T13:59:24Z", "hours": 10.0189, "text": "Since the previous dev log, I worked on fixing the remaining issues in the anonymization workflow and finishing the frontend around the application. The biggest thing I worked on was selected video anonymization. There were still some problems where selected areas could stop being anonymized correctly or the anonymization could move away from the actual object as it moved through the video or retracking when something like a face was lost. I spent quite a lot of time testing different things, tracking down where the problems were coming from, and making the selected anonymization much more reliable across video frames. This ended up taking a lot more work than I initially expected, but the selected video workflow is now working much better. It still isn’t perfect and there are still some bugs and I think I probably need a lot more time to find and fix them but right now it is in a decent working state so I don’t plan to spend more time on achieving perfection because debugging and stuff is boring as hell. I also made some improvements to the overall frontend and added the remaining main pages. I added the about, arivacy, documentation pages and also did some other improvents around the frontend. The documentation now explains the available endpoints, detection targets, anonymization modes and other features. At this point, I feel like the main application is in a really good place. The core functionality is implemented, the main frontend workflow is complete, and the remaining work is mostly getting this thing hosted and stuff. Next, I plan to write the README file, host the application, and do a final round of testing and cleanup. After that, I think I’ll consider this project finished for a while (I will work on improving it further later) and move on to building something new."}, {"post_id": 45041, "ts": "2026-08-11T08:51:50Z", "hours": 10.0544, "text": "Since the previous dev log, I continued building the application workflow and have almost completed the anonymization process. I first worked on the anonymization routes and updated the backend so that the frontend can send detected regions back to the API and have them anonymized. This allows the user to choose which detections they want to anonymize instead of always having to process everything. I also added support for passing dynamic padding (and also made it so that other dynamic stuff is easy to add later) through the request so that the effects can be customized based on what the user wants. I also added client side file handling required for the results page. The original and processed files are kept available in the browser so they can be previewed and downloaded without needing to be stored on the server. Everything also handles missing files and stuff without breaking and I also added error handling with notifications which are displayed to the user. I also made a bunch of smaller UI improvements while connecting everything together. At this point, the main frontend workflow is coming together. Next I will fix small remaining issues and improve reliability rather than adding any major change. The next things I plan to work on are to mainly fix bugs in the code. Like GIFs still need some additional handling to make sure detections are applied across the animation rather than being treated like a normal image and the existing video tracking system is able to follow detections across frames, but during testing I noticed that the anonymization can drift away from the actual detected regions in videos. This is the major thing I need to fix. And I also plan to do some small changes to the UI and stuff and fix some bugs there before adding the remaining pages like the API and documentation pages."}, {"post_id": 44018, "ts": "2026-08-09T14:49:05Z", "hours": 8.77, "text": "Since the previous dev log, I continued building the application workflow and moved beyond the initial upload interface into review/customization functionality. I first worked on the file preview system so that uploaded files can be displayed correctly even when the browser cannot natively display them. Now a preview is generated for unsupported images and documents while keeping browser previews for files that are already supported. After that, I built the results page, which is where the user can review the detections found in their uploaded file and also customise stuff for anonymisation. The biggest addition was the detection review panel. Instead of simply processing everything automatically, the interface now shows the detected content which will allow users to review individual detections and choose which ones they want to work with. A lot of the work also went into the smaller UI/UX improvements. I also fixed a ton of issues that came up while connecting the different pieces together and handling the detection data and working with different file formats. At this point, the frontend is starting to really come together and look like a real app. The next step is to connect stuff to my anonymization API. The user will be able to to select specific detections, choose how they want them anonymized, and add other customizations."}, {"post_id": 43070, "ts": "2026-08-08T04:16:11Z", "hours": 7.8842, "text": "Since the previous dev log, I continued working on the frontend and have now finished the main landing page as well as the initial anonymize/file upload page. I first started by completing the remaining parts of the landing page by adding the footer, API description section, and some final component layout and CSS improvements. After that, I started building the anonymize page, which is where the actual application workflow begins. I implemented the complete upload interface with both file selection and drag and drop feature. I also added client side file handling so uploaded files can be passed from the landing page to the anonymize page without having to upload them to the server first. To do this, I implemented a small IndexedDB based file storage system in the browser. Uploaded files are temporarily stored locally with a timestamp allowing the anonymize page to retrieve the file when the user arrives there. The stored file automatically expires after a short period and is also removed once it has been retrieved. This keeps the upload flow client side and also avoids putting the file data into the URL or sending it to the backend before processing when not needed. The anonymize page now also handles different file types correctly and displays a preview based on their type. I also added different anonymization options making it straightforward to pass the processing preferences to the backend. I too spent time fixing bugs and adding small UX improvements across the new frontend functionality. Next I am going to connect the interface to the backend API and build the actual processing experience. Rather than anonymizing everything automatically, I will create a processing page where the user will be able to decide what to anonymize and I will probably add more customization."}, {"post_id": 41878, "ts": "2026-08-06T11:22:26Z", "hours": 9.1442, "text": "Since the previous dev log I’ve been completely focused on the frontend. I first set up the frontend stack by integrating Tailwind CSS for styles and Alpine.js for lightweight interactivity. I also created a reusable layout with shared components such as the header that future pages can be built consistently and easily. With that foundation in place, I began implementing the landing page. I designed and built the hero section, which consists of a cool drag and drop upload area, and an interactive before/after comparison slider that allows users to visually compare original and anonymized images, which makes the landing page looks awesome. Besides frontend work, I made a few backend improvements as well, including cleaning up temporary video files after processing to avoid leaving unnecessary files behind as originally they weren’t properly disposed of. Next I plan to continue working on the landing page for a bit by adding a footer and maybe some other useful section before connecting everything to the existing API and implementing the upload and processing workflow and building the remaining pages needed."}, {"post_id": 40707, "ts": "2026-08-04T12:12:59Z", "hours": 5.1222, "text": "I focused on preparing everything for the frontend rather than adding new features. I reorganized the API by moving all routes into a dedicated package and grouping every endpoint under /api/v1. This gives everything a much cleaner structure and makes everything look professional. I also added detection endpoints for all supported files. Before the project was primarily focused on anonymization, and even though I had a detection endpoint, it only supported images and was buggy. These new endpoints allows receiving detection results directly without applying any effects and serve as a foundation for selective anonymization that I will implement later. While implementing these endpoints, I refactored how detection IDs are handled across the application. Before they too were buggy but I got them to work. I not only worked on the backend but also worked on the frontend by laying out it’s initial structure that I will build upon. I also spent some time cleaning up the codebase by removing unused imports and simplifying parts of the project. Now I plan to really start working on building the frontend. Now that the API structure is in place, I want to focus on creating a professional looking interface that allows users to interact with all of the functionality that has been implemented so far plus more."}, {"post_id": 40104, "ts": "2026-08-03T14:28:55Z", "hours": 7.8764, "text": "I expanded the range of file types that the application supports. Image support now includes TIFF/TIF, HEIC/HEIF, and GIF files. HEIC/HEIF images are automatically converted to PNG after processing since they are not widely supported for encoding. I also added support for documents which include PDF, DOCX, PPTX, and XLSX files. Rather than building separate anonymization logic for every Office file format, they are first converted to PDF and then are annonymized the same way as PDFs which keeps the implementation much simpler. While working on adding support for these files, I noticed that I had a bunch of duplicated code. So I spent some time refactoring stuff and removing duplicated stuff. I also found that GIF processing was slow so I worked on making it faster by treating it the same as a video and using trackers on it rather than running detection on every frame. I also made few smaller improvements, including adjusting the confidence thresholds for some of the detectors since I was noticing a bunch of false positives and fixing several bugs that came along the way. The next thing I plan to work on are cleaning up the code by finding and removing unused stuff, and then building the frontend for the application."}, {"post_id": 39358, "ts": "2026-08-02T09:02:20Z", "hours": 4.3503, "text": "I focused on improving the performance and maintainability of the anonymization system rather than adding new features. The biggest change was a refactor of the video anonymization system. Instead of repeatedly creating new anonymization masks for every frame, the tracker now caches and reuses them wherever possible. This reduces unnecessary work during video processing and also makes it easier in the future to add further performance improvements. I also improved the applications behavior by preventing both full text anonymization and PII anonymization to be selected at the same time since these targets overlap and would produce redundant processing. I also cleaned up several parts of the codebase by removing unused code and making the everything a bit easier to maintain. I also spent a lot of time experimenting with more aggressive optimization techniques but after testing, I decided to postpone those changes because they added complexity without providing the improvement I was looking for (and they were kind of buggy and it would take a lot more time to fix them). Instead, I decided to focus on optimizations that are simpler. Right now I don’t plan to work on improving the performance (even though there probably are tons of improvements to be made) because that stuff is boring and difficult as hell. Instead I’m going to add support for more file types like PDF."}, {"post_id": 38921, "ts": "2026-08-01T14:49:34Z", "hours": 4.2206, "text": "I expanded the anonymization system by adding multiple anonymization modes instead of having only the pixelization effect. The application can now switch between anonymizing regions using blur, solid fill, noise, emoji overlay, and inpainting. The emoji overlay right now is pretty basic but I plan on improving it later. I also improved the quality of face anonymization by replacing rectangular masks with elliptical masks (for some of the effects), producing better looking results. I also refactored the anonymization pipeline to separate anonymization effects from overlay rendering making future additions even easier and also cleaned up and reorganized the existing image effect implementations to improve readability and consistency across the project. Next, I plan to improve the performance of the anonymization system by trying to reuse detection targets and effect masks wherever possible."}, {"post_id": 38713, "ts": "2026-08-01T08:01:59Z", "hours": 2.0572, "text": "I improved the overall quality and efficiency of the video anonymization system. I updated detection intervals to use the video’s frame rate instead of relying on a hard coded number of frames. This allows the system to adapt well to videos with different frame rates. I also evaluated several object tracking algorithms, including DaSiamRPN, to find a good one that balances accuracy and speed. After comparing the performance of a bunch of them, I switched to Nano Tracker which was faster than my original KCF and CSRT trackers while being more accurate. I made additional improvements to privacy by removing metadata from exported videos. I am now also applying EXIF orientation before processing ensuring that images taken from different devices are processed in their correct orientation. Next, I plan to introduce different anonymization modes like blur and solid black boxes."}, {"post_id": 38633, "ts": "2026-08-01T04:05:36Z", "hours": 6.9461, "text": "I focused on adding support for videos. I first implemented a basic video anonymization system so uploaded videos could be processed the same way as images. I also made sure that the audio for the video is preserved so that it carries over to the anonymized video being unchanged. After getting the basic system working, I improved how videos are processed by adding object tracking and scene change detection. Instead of running detections on every frame, the system can now follow previously detected objects across frames and automatically run detections again when needed (when a tracker is lost, or the scene changes drastically, or periodically after a set amount of frames). This provides a great performance boost while the result stays reliable. Next, I want to continue improving video processing by optimizing it further and making it more accurate."}, {"post_id": 37518, "ts": "2026-07-30T10:23:19Z", "hours": 3.8147, "text": "I focused on improving the entire OCR system, as the previous approach with EasyOCR was missing too much text and reducing the accuracy of the entire system. After spending quite a bit of time trying to improve it, I decided to replace it with PaddleOCR. Now more accurate text detections are being produced making the entire program more robust. After, I expanded the regex based detectors to also account for URLs in addition to emails, etc. I also improved the phone number detection logic to reduce false positives and improve overall accuracy. Then, I adjusted padding around blurred regions so that stuff is covered more consistently and without blurring unnecessary content. The last thing I did was optimizing PaddleOCR as it was much slower than EasyOCR. After a lot of tinkering I was able to significantly reduce its processing time. For some files it now performs at roughly the same speed as EasyOCR while being way more accurate. Next, I probably want to add support for more file types and ultimately videos. And later also support for multiple languages."}, {"post_id": 36966, "ts": "2026-07-29T12:14:19Z", "hours": 3.4994, "text": "I added a new way for the app to detect personal information by using named entity recognition. Previously, the app could only find structured information like emails and phone numbers, but it could not detect things like names. So I integrated Presidio to identify such information from the text extracted from images. These extractions are then combined with the existing PII detections done using regex for the structured stuff. This improves the accuracy of the entire system by combining multiple detection methods instead of relying on one single approach. Next, I will improve the detection process further as right now it isn’t too accurate, mostly because I think the OCR is missing some stuff from the text. So I might try replacing it or find some other way."}, {"post_id": 36365, "ts": "2026-07-28T13:10:24Z", "hours": 6.6581, "text": "I implemented PII detection, allowing the app to identify and blur sensitive information within the text extracted from images instead of blurring everything blindly. First I used regular expressions to detect structured data such as email addresses, phone numbers, etc. But OCR frequently omitted punctuation, causing many valid emails to be missed. To fix this, I redesigned the everything around an OCR token graph. Each token stores its bounding box, neighboring tokens, and line information, allowing the detections to be more accurate. I might switch to PaddleOCR again or someother ocr so that I get more accurate detections. I also spent time improving the overall design of the PII detection system. These changes make it easier to extend in the future while improving the accuracy of the detected regions that are blurred. Next, I will implement NER based detection to identify unstructured personal information such as names."}, {"post_id": 21527, "ts": "2026-07-03T15:19:19Z", "hours": 5.6331, "text": "I implemented OCR-based text detection, allowing the application to detect text within uploaded images. I initially used PaddleOCR, but found that it took over 30 seconds to process a single image. After spending a lot of time trying to optimize its performance, I decided to switch to EasyOCR, which reduced the processing time to around one second while maintaining the same detection quality. Switching the OCR engines was well worth it. I also tried to keep the OCR implementation consistent with the face and license plate detectors and also optimized them too. Next, I will implement detection of sensitive information within the extracted text using regular expressions and NER. This will allow the application to distinguish ordinary text from personal information such as names, emails, phone numbers, etc, and allow me to blur only the relevant regions (right now the the entirety of the detected text is blurred)."}, {"post_id": 20974, "ts": "2026-07-02T18:27:02Z", "hours": 3.4869, "text": "I expanded the application by adding support for license plate detection and blurring. The app can now detect both faces and license plates in an image and blur them. I also made the API more flexible by allowing users to choose what they want to anonymize. Instead of always processing everything, they can now select whether to detect faces, license plates, or both. This makes the application more efficient and easier to extend with new features in the future. I also improved the structure of the project so that adding new types of detections will require much less work. I also added unique ids to each detection, which will be useful for future features that let users blur individual detections. Next, I will implement detection of sensitive text within images. This will allow the app to identify information such as email addresses, phone numbers, and other personal details, then blur them."}, {"post_id": 20600, "ts": "2026-07-02T07:10:58Z", "hours": 1.7692, "text": "I focused on improving the backend rather than adding new features. I reorganized the API by splitting it into smaller, more focused endpoints. For example, there’s now a dedicated endpoint that only detects faces and returns their locations, while separate endpoints handle image anonymization and API health checks. This makes the API easier to use and gives me more flexibility as I add new features. I also reorganized the detection system so that all object detection goes through a single service. At the moment it only detects faces, but it’s now much easier to add support for other types, such as license plates, without having to redesign the rest of the application. Health check endpoints let the application report whether it’s running correctly and whether the AI models have loaded successfully. Next I’m going to add detection beyond faces by adding license plate detection. These changes are gonna make it much easier for me to do so."}, {"post_id": 20144, "ts": "2026-07-01T18:06:28Z", "hours": 2.2011, "text": "I spent some time refining the face anonymization system. The pixelation effect was completely reworked to produce smoother, better looking results while blurring the faces more accurately (using the previous blur you could sometimes make out the features of the people). I also cleaned up the implementation, making the blur functionality much simpler and easier to maintain and most importantly I made it more reusable for ease of use in the future for more features. Behind the scenes, I also reorganized the project’s Python environment by removing unused libraries and recreating the virtual environment. Next I am planning to either add support for videos or make the API better by creating more focused routes such as one that only detects faces and other that returns the blur image. I also want to add functionality where the user can select which people to anonymize instead of blurring everybody detected. After that, I’ll expand beyond simple face anonymization by adding support for automatically detecting and anonymizing other sensitive information, such as license plates, documents, and other personally identifiable content."}, {"post_id": 19929, "ts": "2026-07-01T11:30:11Z", "hours": 4.4158, "text": "Replaced the temporary grayscale filter with automatic face anonymization. Uploaded images are now scanned for faces, and every detected face is pixelated. I also improved detection accuracy to reduce missed faces by tweaking a bunch of stuff for a while and adding more functionality to my face detection like running the image at multiple sizes. A lot of my time was also spent making the pixelated effect look good. I first went for only blurring the faces but now the face is pixelated along with a smooth transition into the surrounding image making the effect look pretty good. Right now images with large crowds are correctly blurred, including people who are far away or appear very small in the image."}, {"post_id": 19370, "ts": "2026-06-30T17:34:05Z", "hours": 1.9744, "text": "Integrated OpenCV so uploaded images are now decoded, processed, and returned to the client. I also introduced modularity into the code by creating separate modules for each step. The grayscale filter is only a temporary placeholder before I implement functionality such as blurring faces."}, {"post_id": 19094, "ts": "2026-06-30T09:42:15Z", "hours": 0.4869, "text": "Added image upload functionality with validation for missing files and non image uploads. The API right now only returns basic metadata about the upload. Next I’ll integrate OpenCV to begin the image processing stage."}, {"post_id": 19079, "ts": "2026-06-30T08:42:18Z", "hours": 0.6667, "text": "Set up the project’s foundation by creating Flask application structure, initializing Git, creating a virtual environment, and organizing the codebase for future features. I also got the development server running to verify everything is wired up correctly. Next, I’ll implement image uploads."}]...
|
Title: Blur Stuff by @AbdurRehman | Stardance
Dev Title: Blur Stuff by @AbdurRehman | Stardance
Devlogs:
Set up the project’s foundation by creating Flask application structure, initializing Git, creating a virtual environment, and organizing the codebase for future features. I also got the development server running to verify everything is wired up correctly. Next, I’ll implement image uploads.
Added image upload functionality with validation for missing files and non image uploads. The API right now only returns basic metadata about the upload. Next I’ll integrate OpenCV to begin the image processing stage.
Integrated OpenCV so uploaded images are now decoded, processed, and returned to the client. I also introduced modularity into the code by creating separate modules for each step. The grayscale filter is only a temporary placeholder before I implement functionality such as blurring faces.
Replaced the temporary grayscale filter with automatic face anonymization. Uploaded images are now scanned for faces, and every detected face is pixelated. I also improved detection accuracy to reduce missed faces by tweaking a bunch of stuff for a while and adding more functionality to my face detection like running the image at multiple sizes. A lot of my time was also spent making the pixelated effect look good. I first went for only blurring the faces but now the face is pixelated along with a smooth transition into the surrounding image making the effect look pretty good. Right now images with large crowds are correctly blurred, including people who are far away or appear very small in the image.
I spent some time refining the face anonymization system. The pixelation effect was completely reworked to produce smoother, better looking results while blurring the faces more accurately (using the previous blur you could sometimes make out the features of the people). I also cleaned up the implementation, making the blur functionality much simpler and easier to maintain and most importantly I made it more reusable for ease of use in the future for more features. Behind the scenes, I also reorganized the project’s Python environment by removing unused libraries and recreating the virtual environment. Next I am planning to either add support for videos or make the API better by creating more focused routes such as one that only detects faces and other that returns the blur image. I also want to add functionality where the user can select which people to anonymize instead of blurring everybody detected. After that, I’ll expand beyond simple face anonymization by adding support for automatically detecting and anonymizing other sensitive information, such as license plates, documents, and other personally identifiable content.
I focused on improving the backend rather than adding new features. I reorganized the API by splitting it into smaller, more focused endpoints. For example, there’s now a dedicated endpoint that only detects faces and returns their locations, while separate endpoints handle image anonymization and API health checks. This makes the API easier to use and gives me more flexibility as I add new features. I also reorganized the detection system so that all object detection goes through a single service. At the moment it only detects faces, but it’s now much easier to add support for other types, such as license plates, without having to redesign the rest of the application. Health check endpoints let the application report whether it’s running correctly and whether the AI models have loaded successfully. Next I’m going to add detection beyond faces by adding license plate detection. These changes are gonna make it much easier for me to do so.
I expanded the application by adding support for license plate detection and blurring. The app can now detect both faces and license plates in an image and blur them. I also made the API more flexible by allowing users to choose what they want to anonymize. Instead of always processing everything, they can now select whether to detect faces, license plates, or both. This makes the application more efficient and easier to extend with new features in the future. I also improved the structure of the project so that adding new types of detections will require much less work. I also added unique ids to each detection, which will be useful for future features that let users blur individual detections. Next, I will implement detection of sensitive text within images. This will allow the app to identify information such as email addresses, phone numbers, and other personal details, then blur them.
I implemented OCR-based text detection, allowing the application to detect text within uploaded images. I initially used PaddleOCR, but found that it took over 30 seconds to process a single image. After spending a lot of time trying to optimize its performance, I decided to switch to EasyOCR, which reduced the processing time to around one second while maintaining the same detection quality. Switching the OCR engines was well worth it. I also tried to keep the OCR implementation consistent with the face and license plate detectors and also optimized them too. Next, I will implement detection of sensitive information within the extracted text using regular expressions and NER. This will allow the application to distinguish ordinary text from personal information such as names, emails, phone numbers, etc, and allow me to blur only the relevant regions (right now the the entirety of the detected text is blurred).
I implemented PII detection, allowing the app to identify and blur sensitive information within the text extracted from images instead of blurring everything blindly. First I used regular expressions to detect structured data such as email addresses, phone numbers, etc. But OCR frequently omitted punctuation, causing many valid emails to be missed. To fix this, I redesigned the everything around an OCR token graph. Each token stores its bounding box, neighboring tokens, and line information, allowing the detections to be more accurate. I might switch to PaddleOCR again or someother ocr so that I get more accurate detections. I also spent time improving the overall design of the PII detection system. These changes make it easier to extend in the future while improving the accuracy of the detected regions that are blurred. Next, I will implement NER based detection to identify unstructured personal information such as names.
I added a new way for the app to detect personal information by using named entity recognition. Previously, the app could only find structured information like emails and phone numbers, but it could not detect things like names. So I integrated Presidio to identify such information from the text extracted from images. These extractions are then combined with the existing PII detections done using regex for the structured stuff. This improves the accuracy of the entire system by combining multiple detection methods instead of relying on one single approach. Next, I will improve the detection process further as right now it isn’t too accurate, mostly because I think the OCR is missing some stuff from the text. So I might try replacing it or find some other way.
I focused on improving the entire OCR system, as the previous approach with EasyOCR was missing too much text and reducing the accuracy of the entire system. After spending quite a bit of time trying to improve it, I decided to replace it with PaddleOCR. Now more accurate text detections are being produced making the entire program more robust. After, I expanded the regex based detectors to also account for URLs in addition to emails, etc. I also improved the phone number detection logic to reduce false positives and improve overall accuracy. Then, I adjusted padding around blurred regions so that stuff is covered more consistently and without blurring unnecessary content. The last thing I did was optimizing PaddleOCR as it was much slower than EasyOCR. After a lot of tinkering I was able to significantly reduce its processing time. For some files it now performs at roughly the same speed as EasyOCR while being way more accurate. Next, I probably want to add support for more file types and ultimately videos. And later also support for multiple languages.
I focused on adding support for videos. I first implemented a basic video anonymization system so uploaded videos could be processed the same way as images. I also made sure that the audio for the video is preserved so that it carries over to the anonymized video being unchanged. After getting the basic system working, I improved how videos are processed by adding object tracking and scene change detection. Instead of running detections on every frame, the system can now follow previously detected objects across frames and automatically run detections again when needed (when a tracker is lost, or the scene changes drastically, or periodically after a set amount of frames). This provides a great performance boost while the result stays reliable. Next, I want to continue improving video processing by optimizing it further and making it more accurate.
I improved the overall quality and efficiency of the video anonymization system. I updated detection intervals to use the video’s frame rate instead of relying on a hard coded number of frames. This allows the system to adapt well to videos with different frame rates. I also evaluated several object tracking algorithms, including DaSiamRPN, to find a good one that balances accuracy and speed. After comparing the performance of a bunch of them, I switched to Nano Tracker which was faster than my original KCF and CSRT trackers while being more accurate. I made additional improvements to privacy by removing metadata from exported videos. I am now also applying EXIF orientation before processing ensuring that images taken from different devices are processed in their correct orientation. Next, I plan to introduce different anonymization modes like blur and solid black boxes.
I expanded the anonymization system by adding multiple anonymization modes instead of having only the pixelization effect. The application can now switch between anonymizing regions using blur, solid fill, noise, emoji overlay, and inpainting. The emoji overlay right now is pretty basic but I plan on improving it later. I also improved the quality of face anonymization by replacing rectangular masks with elliptical masks (for some of the effects), producing better looking results. I also refactored the anonymization pipeline to separate anonymization effects from overlay rendering making future additions even easier and also cleaned up and reorganized the existing image effect implementations to improve readability and consistency across the project. Next, I plan to improve the performance of the anonymization system by trying to reuse detection targets and effect masks wherever possible.
I focused on improving the performance and maintainability of the anonymization system rather than adding new features. The biggest change was a refactor of the video anonymization system. Instead of repeatedly creating new anonymization masks for every frame, the tracker now caches and reuses them wherever possible. This reduces unnecessary work during video processing and also makes it easier in the future to add further performance improvements. I also improved the applications behavior by preventing both full text anonymization and PII anonymization to be selected at the same time since these targets overlap and would produce redundant processing. I also cleaned up several parts of the codebase by removing unused code and making the everything a bit easier to maintain. I also spent a lot of time experimenting with more aggressive optimization techniques but after testing, I decided to postpone those changes because they added complexity without providing the improvement I was looking for (and they were kind of buggy and it would take a lot more time to fix them). Instead, I decided to focus on optimizations that are simpler. Right now I don’t plan to work on improving the performance (even though there probably are tons of improvements to be made) because that stuff is boring and difficult as hell. Instead I’m going to add support for more file types like PDF.
I expanded the range of file types that the application supports. Image support now includes TIFF/TIF, HEIC/HEIF, and GIF files. HEIC/HEIF images are automatically converted to PNG after processing since they are not widely supported for encoding. I also added support for documents which include PDF, DOCX, PPTX, and XLSX files. Rather than building separate anonymization logic for every Office file format, they are first converted to PDF and then are annonymized the same way as PDFs which keeps the implementation much simpler. While working on adding support for these files, I noticed that I had a bunch of duplicated code. So I spent some time refactoring stuff and removing duplicated stuff. I also found that GIF processing was slow so I worked on making it faster by treating it the same as a video and using trackers on it rather than running detection on every frame. I also made few smaller improvements, including adjusting the confidence thresholds for some of the detectors since I was noticing a bunch of false positives and fixing several bugs that came along the way. The next thing I plan to work on are cleaning up the code by finding and removing unused stuff, and then building the frontend for the application.
I focused on preparing everything for the frontend rather than adding new features. I reorganized the API by moving all routes into a dedicated package and grouping every endpoint under /api/v1. This gives everything a much cleaner structure and makes everything look professional. I also added detection endpoints for all supported files. Before the project was primarily focused on anonymization, and even though I had a detection endpoint, it only supported images and was buggy. These new endpoints allows receiving detection results directly without applying any effects and serve as a foundation for selective anonymization that I will implement later. While implementing these endpoints, I refactored how detection IDs are handled across the application. Before they too were buggy but I got them to work. I not only worked on the backend but also worked on the frontend by laying out it’s initial structure that I will build upon. I also spent some time cleaning up the codebase by removing unused imports and simplifying parts of the project. Now I plan to really start working on building the frontend. Now that the API structure is in place, I want to focus on creating a professional looking interface that allows users to interact with all of the functionality that has been implemented so far plus more.
Since the previous dev log I’ve been completely focused on the frontend. I first set up the frontend stack by integrating Tailwind CSS for styles and Alpine.js for lightweight interactivity. I also created a reusable layout with shared components such as the header that future pages can be built consistently and easily. With that foundation in place, I began implementing the landing page. I designed and built the hero section, which consists of a cool drag and drop upload area, and an interactive before/after comparison slider that allows users to visually compare original and anonymized images, which makes the landing page looks awesome. Besides frontend work, I made a few backend improvements as well, including cleaning up temporary video files after processing to avoid leaving unnecessary files behind as originally they weren’t properly disposed of. Next I plan to continue working on the landing page for a bit by adding a footer and maybe some other useful section before connecting everything to the existing API and implementing the upload and processing workflow and building the remaining pages needed.
Since the previous dev log, I continued working on the frontend and have now finished the main landing page as well as the initial anonymize/file upload page. I first started by completing the remaining parts of the landing page by adding the footer, API description section, and some final component layout and CSS improvements. After that, I started building the anonymize page, which is where the actual application workflow begins. I implemented the complete upload interface with both file selection and drag and drop feature. I also added client side file handling so uploaded files can be passed from the landing page to the anonymize page without having to upload them to the server first. To do this, I implemented a small IndexedDB based file storage system in the browser. Uploaded files are temporarily stored locally with a timestamp allowing the anonymize page to retrieve the file when the user arrives there. The stored file automatically expires after a short period and is also removed once it has been retrieved. This keeps the upload flow client side and also avoids putting the file data into the URL or sending it to the backend before processing when not needed. The anonymize page now also handles different file types correctly and displays a preview based on their type. I also added different anonymization options making it straightforward to pass the processing preferences to the backend. I too spent time fixing bugs and adding small UX improvements across the new frontend functionality. Next I am going to connect the interface to the backend API and build the actual processing experience. Rather than anonymizing everything automatically, I will create a processing page where the user will be able to decide what to anonymize and I will probably add more customization.
Since the previous dev log, I continued building the application workflow and moved beyond the initial upload interface into review/customization functionality. I first worked on the file preview system so that uploaded files can be displayed correctly even when the browser cannot natively display them. Now a preview is generated for unsupported images and documents while keeping browser previews for files that are already supported. After that, I built the results page, which is where the user can review the detections found in their uploaded file and also customise stuff for anonymisation. The biggest addition was the detection review panel. Instead of simply processing everything automatically, the interface now shows the detected content which will allow users to review individual detections and choose which ones they want to work with. A lot of the work also went into the smaller UI/UX improvements. I also fixed a ton of issues that came up while connecting the different pieces together and handling the detection data and working with different file formats. At this point, the frontend is starting to really come together and look like a real app. The next step is to connect stuff to my anonymization API. The user will be able to to select specific detections, choose how they want them anonymized, and add other customizations.
Since the previous dev log, I continued building the application workflow and have almost completed the anonymization process. I first worked on the anonymization routes and updated the backend so that the frontend can send detected regions back to the API and have them anonymized. This allows the user to choose which detections they want to anonymize instead of always having to process everything. I also added support for passing dynamic padding (and also made it so that other dynamic stuff is easy to add later) through the request so that the effects can be customized based on what the user wants. I also added client side file handling required for the results page. The original and processed files are kept available in the browser so they can be previewed and downloaded without needing to be stored on the server. Everything also handles missing files and stuff without breaking and I also added error handling with notifications which are displayed to the user. I also made a bunch of smaller UI improvements while connecting everything together. At this point, the main frontend workflow is coming together. Next I will fix small remaining issues and improve reliability rather than adding any major change. The next things I plan to work on are to mainly fix bugs in the code. Like GIFs still need some additional handling to make sure detections are applied across the animation rather than being treated like a normal image and the existing video tracking system is able to follow detections across frames, but during testing I noticed that the anonymization can drift away from the actual detected regions in videos. This is the major thing I need to fix. And I also plan to do some small changes to the UI and stuff and fix some bugs there before adding the remaining pages like the API and documentation pages.
Since the previous dev log, I worked on fixing the remaining issues in the anonymization workflow and finishing the frontend around the application. The biggest thing I worked on was selected video anonymization. There were still some problems where selected areas could stop being anonymized correctly or the anonymization could move away from the actual object as it moved through the video or retracking when something like a face was lost. I spent quite a lot of time testing different things, tracking down where the problems were coming from, and making the selected anonymization much more reliable across video frames. This ended up taking a lot more work than I initially expected, but the selected video workflow is now working much better. It still isn’t perfect and there are still some bugs and I think I probably need a lot more time to find and fix them but right now it is in a decent working state so I don’t plan to spend more time on achieving perfection because debugging and stuff is boring as hell. I also made some improvements to the overall frontend and added the remaining main pages. I added the about, arivacy, documentation pages and also did some other improvents around the frontend. The documentation now explains the available endpoints, detection targets, anonymization modes and other features. At this point, I feel like the main application is in a really good place. The core functionality is implemented, the main frontend workflow is complete, and the remaining work is mostly getting this thing hosted and stuff. Next, I plan to write the README file, host the application, and do a final round of testing and cleanup. After that, I think I’ll consider this project finished for a while (I will work on improving it further later) and move on to building something new.
Since the previous dev log, I have been focused on getting Blur Stuff ready for deployment. This ended up being much more painful than I expected. The biggest part was dealing with dependency issues. The project worked locally, but moving everything to a production environment exposed a bunch of problems with Python versions, PaddlePaddle, PaddleOCR, PaddleX, PyYAML, spaCy, OpenCV and other dependencies. I spent a lot of time testing different configurations, fixing dependency conflicts, debugging the production container, and figuring out why things that seemed to work okay locally were breaking during deployment. The biggest headache was PaddleOCR. I had to deal with missing system libraries, model downloading, Paddle initialization issues and eventually a oneDNN runtime error that was causing OCR requests to return 503s. After a lot of debugging and testing different configurations, I eventually got the OCR pipeline working properly in production. That was probably the one of the most frustrating parts of the entire project, but it is finally done. I also switched the spaCy model from en_core_web_lg to en_core_web_sm. The larger model was unnecessarily heavy for what Blur Stuff needs and was making deployments take way longer. Switching to the smaller model made deployment faster and also improved performance without having a noticeable impact on the PII detection quality. After successful deployment I finished the README and added the final assets, screenshots, logo and documentation. I added the MIT license, favicon, deployment related changes and generally cleaned up the entire repository. At this point, I think Blur Stuff is ready to ship. There are definitely still improvements I could make, especially improving performance, making video anonymization more reliable and efficient, and polishing various parts of the application but I don’t want to spend more time trying to make everything perfect. The current deployment is also running on Railway’s free plan, so there are some resource and memory limitations since Blur Stuff runs several machine learning models in the same process. For now, I’m okay with that rather than spending more time optimizing around the limitations of a free deployment. For now, I’m calling it finished and shipping it since the core functionality is working and the project is deployed. I’ll definitely come back to Blur Stuff later to improve it, but I think it’s time to move on to something new.
Since shipping Blur Stuff, I got feedback that the frontend felt too generic so I completely revamped the UI to give it a more intentional and fun style. I redesigned everything from colors to typography and everything else. Now the app has a more personal feel. I also cleaned up some small things here and there.
Ships:
I made Blur Stuff, a privacy focused tool for safely sharing images, videos, GIFs, and documents. It automatically detects sensitive information such as faces, license plates, text, and PII, then lets you anonymize the detected areas with different anonymization effects. It also removes EXIF metadata so other sensitive information isn’t left hidden inside the file. The goal of this project is to make sharing media safely as easy as uploading it. Instead of manually searching through a file like a document or photo and manually covering stuff you don’t want others to see, Blur Stuff does this work for you. The hardest part of making this project was making anonymization work reliably across different types of media. Images were relatively simple, but videos and GIFs require frame by frame processing while keeping detections consistent over time. And to make things efficient and fast I also had to deal with things like tracking which were really tricky. Connecting the frontend and backend was also another challenge and then hosting everything also brought it’s problems with dependency conflicts which took quite a while to solve. The thing I am most proud of is that this became a real working and professional looking application rather than just a prototype type thing. It supports most of the commonly used media formats, has multiple customization options among other neat functionalities. Also I like how this project is privacy focused. Like it doesn’t store the user’s files ever. It just processes them, removing EXIF, and gives the result. Blur Stuff can be tested by uploading an image containing faces, text, or a license plate and let it detect the sensitive regions and then select what you want anonymized, and run the anonymization. The current deployment has limited memory and resources since I’m on a free plan, so larger videos and documents can take a while to process, and very large files may run into resource limitations. The app is also deployed using Railway’s serverless option, so the server may need a few seconds to start up if it hasn’t received a request recently. Also the selected region anonymization for videos is still a bit buggy but mostly it works. These are areas I will improve in the future....
|
1792849922.2096336
|
1792936322.2096336
|
|
62095
|
violin by @amiterlich05 | Stardance
|
amiterlich05
|
1
|
127.0
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-23T11:38:13Z", "hours": 127.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "I designed and modeled a complete 3D violin from scratch using Onshape. The most challenging part was getting the curved body geometry and intricate details of the violin correct. I am really proud of how the final assembly turned out, and you can test it by opening the public Onshape CAD link.", "repo": "https://github.com/amiterlichpico-beep/violin-onshape", "demo": ""}]...
|
[{"post_id": 72618, "ts": "202 [{"post_id": 72618, "ts": "2026-09-23T11:15:34Z", "hours": 5.5556, "text": "DevLog 9 I’ve finished everything; I’m publishing it now."}, {"post_id": 72520, "ts": "2026-09-23T03:49:08Z", "hours": 9.995, "text": "Devlog 7 I’ve just about finished the final touches; all that’s left is to build the stand and add the very last details. I’ll be releasing it in the next devlog."}, {"post_id": 72324, "ts": "2026-09-22T17:47:09Z", "hours": 4.1386, "text": "Dev Vlog 7 So… I’ve solved the color issue and handled the final touches. I’ll work a bit more on the finishing details, then build a stand to mount it on—and that’s it. It should be ready very soon; I think maybe two more updates from now. Hopefully, it’ll be finished sometime tomorrow."}, {"post_id": 72198, "ts": "2026-09-22T13:14:42Z", "hours": 27.7378, "text": "Developer Vlog 6 I’m almost done; I really just have a few final touches left and need to make it stand upright. I probably won’t have time to add an arch, since I really need to wrap up this project and get back to studying. I don’t know why—I didn’t do anything to it—but it decided to turn purple; I’ll check everything over and then close out the file and the project. So… this is what it looks like right now."}, {"post_id": 71653, "ts": "2026-09-21T07:46:48Z", "hours": 22.2042, "text": "Devlog #5? I’ve more or less put all the parts together; all that’s left is to add textures, finishing touches, and a stand. I’m not sure if I’ll make a violin bow, too—I tried, but I really couldn’t get the hair right; if I do make one, it’ll probably be without the hair."}, {"post_id": 70493, "ts": "2026-09-19T08:50:26Z", "hours": 19.0517, "text": "Developer Vlog #4 I’ve finished making most of the violin parts; now I need to assemble everything and add textures and finishing touches."}, {"post_id": 69477, "ts": "2026-09-17T04:20:07Z", "hours": 10.4853, "text": "devlod#3 I’ve just finished assembling some of the parts and the bottom section of the violin; now, all that’s left is the top section and the final finishing touches."}, {"post_id": 68956, "ts": "2026-09-16T03:29:22Z", "hours": 16.5075, "text": "I continued working on the violin; I had already crafted almost the entire lower half and the ornamentation for the middle section."}, {"post_id": 68483, "ts": "2026-09-15T06:25:07Z", "hours": 11.0475, "text": "Hi, I want to say upfront that this will probably be pretty bad, since it’s the first time I’m writing a dev log like this… Anyway, for this project, I chose to work on a violin model using the web-based software Onshape. I’m roughly halfway through—or maybe just at the beginning… I mean, I have the structure down, but the hard part is still ahead. I need to do the finishing touches to make it look good and check that it’s properly converted into a printable file… uh… Yeah, I don’t have much else to say about it right now. So, sorry."}]...
|
Title: violin by @amiterlich05 | Stardance
Devlog Title: violin by @amiterlich05 | Stardance
Devlogs:
Hi, I want to say upfront that this will probably be pretty bad, since it’s the first time I’m writing a dev log like this… Anyway, for this project, I chose to work on a violin model using the web-based software Onshape. I’m roughly halfway through—or maybe just at the beginning… I mean, I have the structure down, but the hard part is still ahead. I need to do the finishing touches to make it look good and check that it’s properly converted into a printable file… uh… Yeah, I don’t have much else to say about it right now. So, sorry.
I continued working on the violin; I had already crafted almost the entire lower half and the ornamentation for the middle section.
devlod#3 I’ve just finished assembling some of the parts and the bottom section of the violin; now, all that’s left is the top section and the final finishing touches.
Developer Vlog #4 I’ve finished making most of the violin parts; now I need to assemble everything and add textures and finishing touches.
Devlog #5? I’ve more or less put all the parts together; all that’s left is to add textures, finishing touches, and a stand. I’m not sure if I’ll make a violin bow, too—I tried, but I really couldn’t get the hair right; if I do make one, it’ll probably be without the hair.
Developer Vlog 6 I’m almost done; I really just have a few final touches left and need to make it stand upright. I probably won’t have time to add an arch, since I really need to wrap up this project and get back to studying. I don’t know why—I didn’t do anything to it—but it decided to turn purple; I’ll check everything over and then close out the file and the project. So… this is what it looks like right now.
Dev Vlog 7 So… I’ve solved the color issue and handled the final touches. I’ll work a bit more on the finishing details, then build a stand to mount it on—and that’s it. It should be ready very soon; I think maybe two more updates from now. Hopefully, it’ll be finished sometime tomorrow.
Devlog 7 I’ve just about finished the final touches; all that’s left is to build the stand and add the very last details. I’ll be releasing it in the next devlog.
DevLog 9 I’ve finished everything; I’m publishing it now.
Ships:
I designed and modeled a complete 3D violin from scratch using Onshape. The most challenging part was getting the curved body geometry and intricate details of the violin correct. I am really proud of how the final assembly turned out, and you can test it by opening the public Onshape CAD link....
|
1792813913.61649
|
1792900313.61649
|
|
35760
|
Algora by @diputs | Stardance
|
diputs
|
1
|
127.84
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-08-13T06:59:44Z", "hours": 95.0, "multiplier": 18.72, "payout": 0, "blessing": "neutral", "text": "Algora 1.0 Ship An attempt to put everything I wish I had when learning algorithms into one place. This project started when a friend asked me for help with USACO. While helping them, I started thinking back to my own path of learning algorithms and the things I wished I had when I was learning them. I wanted one place where I could not only read about an algorithm, but actually see it work, understand what is happening at each step, compare it with other algorithms, and look at both a teaching implementation and a more practical implementation. That idea eventually became Algora. What’s Included The first release currently includes: 15 sorting algorithms 17 searching algorithms Interactive algorithm visualizations Step by step execution and playback controls Algorithm specific workspaces for more complex algorithms Quick facts and complexity information Detailed algorithm explanations Pseudocode Teaching implementations in C, C++, Java, and Python Production style implementations in C, C++, Java, and Python Sorting algorithm comparison Searching algorithm comparison Comparison information including time complexity, space complexity, requirements, strengths, and weaknesses Warnings when algorithms are not directly comparable Individual pages for every algorithm The goal was to make each algorithm more than just a page of code. I wanted to be able to go from: What is this? → How does it work? → Can I see it happen? → Can I understand each step? → What does the code look like? → How does it compare to other approaches? Why I Built It I built Algora because I kept thinking about how much easier learning algorithms could have been if I had all of these things in one place when I was going through USACO. I’ve gone through the USACO training path myself and reached Platinum, and helping my friend brought me back to the way I learned all of this in the first place. So instead of making another collection of algorithm implementations, I wanted to make the resource I wish I had back then. Algora is an attempt to put everything I wish I had when learning algorithms into one place. The Making of Algora I also recorded a large part of the development process while building the project. The video below is a 1000× speed timelapse made from roughly 70 hours of raw development footage. I lost some footage along the way, so it does not contain absolutely everything, but most of the development process is there. Apparently Stands saw the size of the video and decided it simply did not exist D: If you really want to watch the video, the link is here: https://github.com/diputs-sudo/algora/tree/main/video you might have to download it :( Thank You Thank you to everyone who supported me while I was building Algora. Thank you to everyone who tested things, gave feedback, helped me figure things out, listened to me ramble about algorithms, or simply encouraged me to keep working on it. This project ended up becoming much bigger than the original idea, and I’m really happy that I finally get to share it.", "repo": "https://github.com/diputs-sudo/algora", "demo": "https://diputs-sudo.github.io/algora/"}]...
|
[{"post_id": 52833, "ts": "202 [{"post_id": 52833, "ts": "2026-08-23T05:58:16Z", "hours": 3.8481, "text": "algora dev log #68 Implemented the page for Dijkstra. Finished the algorithm workspace, Both the advanced and teaching version of the algorithm in all 4 languages including python, c, java and c++"}, {"post_id": 52724, "ts": "2026-08-23T01:25:57Z", "hours": 0.5294, "text": "I got my latest project reviews back today, and first of all thank you so much to everyone who took the time to look through the project! A lot of the feedback genuinely made me really happy. Some people pointed out bugs I hadn’t noticed, some gave me ideas for things I could improve, and a few of you even said you wanted to bookmark the project or use it for studying algorithms, which honestly means a lot to me. There were also some questions about the tech stack and how different parts of the project were built, which I’m happy to answer. Review #1: “what is this stack? html, typescript, c, c++ and java?” The website itself is built primarily with HTML, CSS, and TypeScript. The additional languages: C, C++, Java, and Python are there because the project includes implementations of the algorithms in multiple languages. Review #5: “Dijkstra doesn’t work, I get a 404” The Ship is mainly about sorting and searching algorithms. I think by the time you visited the pathfinding page, I had already started working on the new pathfinding section but hadn’t finished implementing it yet, which is why Dijkstra gave you a 404. I’m actively working on the Dijkstra right now tho! Review #8: “the amount of time to type out your project at 70 words per minute is around 200 hours” This assumes every character was typed manually. I use tab completion and copy/paste repetitive code. For example, most of the TypeScript engine API is basically the same across algorithms, so I’m obviously not going to retype the same thing every time Review #8: “make no mention of struggles or frustrations, inspirations or ideas” I personally don’t like putting my frustration into project logs. I usually just keep my devlogs focused on what I made, changed, or learned. Review #8: “You type out rgba and hexadecimals by hand without a tool” I sketched the website layout on my iPad and used it as a reference while coding. I can’t exactly copy/paste an RGB value from my iPad onto my computer, so I just typed it Review #8: “you type out entire svg lines” The SVGs are actually copy/pasted. They’re reused assets and basically the same every time. The timelapse is around 1000x speed, so a paste just looks like the SVG suddenly appeared. Review #8: “You never delete any lines in your timelapses” I cut most of the boring debugging out because who wants to watch that. That’s also why sometimes the timelapse jumps from broken code to fixed code. I wanted the videos to stay short and watchable. Review #8: “Show us your second monitor” There is no second monitor. You assumed there was one because I wasn’t recording my full screen. I intentionally only record the part of my screen relevant to the project because the rest is my privacy. I also look through a lot of references, documentation, and other pages while working, and I don’t think anyone wants all of that flashing across a 1000x timelapse anyway. I don’t mind people questioning my work or asking how I did something, but I do think there’s a difference between asking and immediately assuming someone faked their work. Some of the comments also became pretty personal, which was honestly disappointing to see. Review #9: “Don’t bring it down pls” don’t worry, I’m planning to keep the website up I’m really happy that you actually want to use it though, that’s basically the whole reason I made this project And to everyone else who left a review, thank you so much!! I didn’t want to respond to every review with just “thank you” over and over again, but I read all of them and really appreciate all the feedback Seeing people say they actually want to use the website for learning, revision, or even USACO genuinely made me really happy. That’s basically why I wanted to build this project in the first place"}, {"post_id": 52049, "ts": "2026-08-22T02:52:45Z", "hours": 3.9061, "text": "algora dev log #68 Implemented Python C++ Java and C for D* Lite. Both the advanced and teaching implementation is finished."}, {"post_id": 51461, "ts": "2026-08-21T05:51:58Z", "hours": 4.48, "text": "algora dev log #67 Implemented the page for D* Lite . Finished the algorithm workspace, this one is hard and the TypeScript is behaving weirdly, Gotta rewrite a lot of times for the engine to render correctly with the different heuristic implemented and clicking to change the board while the algorithm runs But it works now. Really proud of it."}, {"post_id": 50795, "ts": "2026-08-20T05:54:40Z", "hours": 2.8314, "text": "algora dev log #66 Implemented the page for Bidirectional BFS. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 50111, "ts": "2026-08-19T05:36:14Z", "hours": 3.2497, "text": "algora dev log #65 Implemented the page for BFS. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 49389, "ts": "2026-08-18T05:43:00Z", "hours": 3.0567, "text": "algora dev log #64 Implemented the page for Bellman-Ford. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 49313, "ts": "2026-08-18T01:54:06Z", "hours": 2.8961, "text": "algora dev log #63 Implemented the page for A* pathfinding. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 47858, "ts": "2026-08-16T02:52:38Z", "hours": 2.9633, "text": "algora dev log #62 Implemented the page for 0-1 BFS. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 47341, "ts": "2026-08-15T07:02:41Z", "hours": 1.1183, "text": "algora dev log # 61 Finished both standard and advanced DFS implementations. Standard version keeps the algorithm clean. Advanced version adds a more optimized/practical implementation with deeper control over traversal"}, {"post_id": 47310, "ts": "2026-08-15T05:48:18Z", "hours": 2.6106, "text": "algora dev log # 60 Built the reusable grid search engine for graph/search algorithms. Finished DFS as the first full test of the engine traversal, animation, visited states, and path reconstruction all working. Added randomized maze generation with start/target. Built and integrated the full DFS algorithm page + interactive workspace. Engine is now ready to reuse for the rest of the search algorithms"}, {"post_id": 46709, "ts": "2026-08-14T04:49:55Z", "hours": 1.3528, "text": "Algora dev log #59 Apparently shipping 1.0 means I can add more things now. Started building out graph algorithms, new index, filters, learning paths, complexity info, and the foundation for the graph workspaces."}, {"post_id": 46178, "ts": "2026-08-13T06:31:08Z", "hours": 0.3875, "text": "#algora dev log #58 Update Readme for the shipping :D The Making of Algora Below is a 1000× speed timelapse of me building the project. I lost some footage along the way, so this isn’t every single thing that happened, but most of the development is here. Altogether, it represents roughly 70 hours of raw development footage condensed into this video. It is kind of crazy to look back at all of that work and see it compressed into a few minutes."}, {"post_id": 46128, "ts": "2026-08-13T04:14:50Z", "hours": 2.1542, "text": "#algora dev log # 57 Finally finished the Algorithm Comparison page for searching algorithm class, Compelted the HTML and TypeScript implementations. Added some details like warnings. Finished with the Search Class of Algorithms 🎉"}, {"post_id": 45583, "ts": "2026-08-12T05:26:59Z", "hours": 3.4308, "text": "algora dev log #55 Upgraded the Exponential Search, Galloping Search, and Jump Search workspace so they are more intuitive. :D and the most time consuming part of upgrading the css"}, {"post_id": 45017, "ts": "2026-08-11T04:52:47Z", "hours": 1.8975, "text": "algora dev log #55 Implemented the page for Rotated Sorted Array Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python."}, {"post_id": 44389, "ts": "2026-08-10T03:07:03Z", "hours": 2.0311, "text": "algora dev log #54 Implemented the page for Quickselect. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python."}, {"post_id": 43681, "ts": "2026-08-09T02:42:06Z", "hours": 2.0572, "text": "algora dev log #53 Implemented the page for Parametric Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python."}, {"post_id": 43072, "ts": "2026-08-08T04:32:03Z", "hours": 1.9503, "text": "algora dev log #52 Implemented the page for Hash Table Lookup. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python."}, {"post_id": 42979, "ts": "2026-08-07T23:56:59Z", "hours": 2.6147, "text": "algora dev log #51 Implemented the page for Fractional Cascading. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python."}, {"post_id": 42364, "ts": "2026-08-07T01:26:27Z", "hours": 1.7836, "text": "algora dev log #50 Finishes and upgrade the page for Bitonic Array Search, added custom workspace for the search"}, {"post_id": 41689, "ts": "2026-08-06T02:07:07Z", "hours": 2.0547, "text": "algora dev log #49 Implemented the page for Uniform Binary Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python."}, {"post_id": 41642, "ts": "2026-08-05T23:59:03Z", "hours": 1.9908, "text": "algora dev log #48 Implemented the page for Ternary Linear Search. Finished both easy and advanced versions, Finished implementations in C, C++, Java, and Python."}, {"post_id": 41070, "ts": "2026-08-05T01:06:29Z", "hours": 2.38, "text": "algora dev log #47 Implemented the page for Sentinel Linear Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python."}, {"post_id": 40527, "ts": "2026-08-04T03:32:57Z", "hours": 2.4717, "text": "algora dev log #46 Implemented the page for Meta Binary Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python."}, {"post_id": 40459, "ts": "2026-08-04T00:04:15Z", "hours": 1.4769, "text": "algora dev log #45 Implemented the page for Linear Search. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 40392, "ts": "2026-08-03T21:13:25Z", "hours": 2.0106, "text": "algora dev log #44 Implemented the page for Jump Search. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 39836, "ts": "2026-08-03T02:50:53Z", "hours": 1.525, "text": "algora dev log #43 Thought the interpolation search lab was done until I actually used it. It turned out it showed the result, but not the reasoning. Added a new visualization that explains how the probe is estimated instead of just where it lands."}, {"post_id": 39804, "ts": "2026-08-03T01:17:01Z", "hours": 1.9919, "text": "algora dev log #42 Implemented the page for Interpolation Search. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 39276, "ts": "2026-08-02T04:32:36Z", "hours": 2.2861, "text": "algora dev log #41 Implemented the page for Galloping Search. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python."}, {"post_id": 39212, "ts": "2026-08-02T01:01:41Z", "hours": 2.2714, "text": "algora dev log #40 Implemented the page for Fibonacci Search. Finished both easy and advanced version of Fibonacci search implementations in C, C++, Java, and Python. Together with the engine"}, {"post_id": 39161, "ts": "2026-08-01T22:06:30Z", "hours": 0.7069, "text": "algora dev log #39 Spent some time implementing the advanced version of Exponential Search. Finished implementations in C, C++, Java, and Python."}, {"post_id": 39123, "ts": "2026-08-01T20:38:05Z", "hours": 0.9406, "text": "algora dev log #38 Spent some time implementing the advanced version of Bitonic Array Search. Finished implementations in C, C++, Java, and Python."}, {"post_id": 39070, "ts": "2026-08-01T19:16:21Z", "hours": 0.6119, "text": "algora dev log #37 Spent some time implementing the advanced version of Binary Search . Finished implementations in C, C++, Java, and Python."}, {"post_id": 38591, "ts": "2026-08-01T02:15:08Z", "hours": 0.6681, "text": "algora dev log #36 Spent some time implementing the advanced version of Tournament Sort . Finished implementations in C, C++, Java, and Python."}, {"post_id": 38566, "ts": "2026-08-01T01:29:13Z", "hours": 0.7253, "text": "algora dev log #35 Spent some time implementing the advanced version of Tim Sort . Finished implementations in C, C++, Java, and Python."}, {"post_id": 38545, "ts": "2026-08-01T00:38:44Z", "hours": 0.6617, "text": "algora dev log #34 Spent some time implementing the advanced version of Shell Sort . Finished implementations in C, C++, Java, and Python."}, {"post_id": 38500, "ts": "2026-07-31T22:30:21Z", "hours": 0.5581, "text": "algora dev log #33 Spent some time implementing the advanced version of Selection Sort . Finished implementations in C, C++, Java, and Python."}, {"post_id": 38484, "ts": "2026-07-31T21:52:30Z", "hours": 0.6564, "text": "algora dev log #32 Spent some time implementing the advanced version of Radix Sort . Finished implementations in C, C++, Java, and Python."}, {"post_id": 38467, "ts": "2026-07-31T21:09:48Z", "hours": 0.8247, "text": "algora dev log #31 Spent some time implementing the advanced version of Quick Sort . Finished implementations in C, C++, Java, and Python."}, {"post_id": 38379, "ts": "2026-07-31T18:44:07Z", "hours": 1.1031, "text": "algora dev log #30 Spent some time implementing the advanced version of Merge Sort (in place) today. Finished implementations in C, C++, Java, and Python. honestly this one is pretty hard to get it right"}, {"post_id": 37976, "ts": "2026-07-31T01:43:35Z", "hours": 0.7942, "text": "algora dev log #29 Spent some time implementing the advanced version of Merge Sort today. Finished implementations in C, C++, Java, and Python."}, {"post_id": 37957, "ts": "2026-07-31T00:38:05Z", "hours": 2.0833, "text": "algora dev log #28 Spent some time implementing the advanced version of Insertion and Intro Sort today. Finished implementations in C, C++, Java, and Python."}, {"post_id": 37430, "ts": "2026-07-30T04:53:22Z", "hours": 0.6233, "text": "#algora dev log #27 Spent some time implementing the advanced version of Heap Sort today. Finished implementations in C, C++, Java, and Python."}, {"post_id": 37420, "ts": "2026-07-30T03:54:46Z", "hours": 0.6931, "text": "algora dev log #26 Spent some time implementing the advanced version of Counting Sort today. Finished implementations in C, C++, Java, and Python."}, {"post_id": 37336, "ts": "2026-07-30T00:02:09Z", "hours": 0.8036, "text": "algora dev log #25 Spent some time implementing the advanced version of Bucket Sort today. Finished implementations in C, C++, Java, and Python."}, {"post_id": 37323, "ts": "2026-07-29T23:10:28Z", "hours": 0.6233, "text": "algora dev log #24 Spent some time implementing the advanced version of Bubble Sort today. Finished implementations in C, C++, Java, and Python."}, {"post_id": 36788, "ts": "2026-07-29T01:59:30Z", "hours": 1.8864, "text": "algora dev log #23 Changed my mind on this one. I want each algorithm page to include a production style implementation so readers can compare a simplified, educational version with a more advanced version that’s closer to what you might actually see in production. Because of that, I went back and started adding Bitonic Sort implementations in all four supported languages."}, {"post_id": 36704, "ts": "2026-07-28T22:39:27Z", "hours": 1.6408, "text": "algora dev log #22 Spent some time building the Exponential Search page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Fibonacci Search!"}, {"post_id": 36567, "ts": "2026-07-28T19:30:18Z", "hours": 2.3331, "text": "algora dev log #22 Spent some time building the Bitonic Array Search page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Exponential Search!"}, {"post_id": 36167, "ts": "2026-07-28T02:34:39Z", "hours": 2.0194, "text": "algora dev log #21 Spent some time building the Binary Search page today! Added the frontend and TypeScript. will finished the python, c, hava, and c++ implementation later!"}, {"post_id": 36125, "ts": "2026-07-28T00:26:15Z", "hours": 0.2853, "text": "algora dev log #20 Adding the missing main page TypeScript, now the animation is working in the main page."}, {"post_id": 36108, "ts": "2026-07-28T00:02:00Z", "hours": 1.3203, "text": "algora dev log #19 Adding the main page for searching algorithms layout, finishing the HTML and TypeScript for the page"}, {"post_id": 35571, "ts": "2026-07-27T03:12:16Z", "hours": 2.0331, "text": "algora dev log #18 Worked on the new algorithm comparison page. You can now compare two sorting algorithms side by side, view their information, watch the visualizations together, and browse the implementations in C, C++, Java, and Python. This ended up being one of the larger frontend pages in Algora so far and took a long time to write the typescript engine."}, {"post_id": 35498, "ts": "2026-07-26T23:37:19Z", "hours": 1.0839, "text": "algora dev log #17 Finished Tournament Sort page today! Added the TypeScript, then finish implementing the algorithm in C, C++, Java, and Python."}, {"post_id": 34950, "ts": "2026-07-26T02:13:55Z", "hours": 0.5944, "text": "algora dev log #16 don’t have much time today but build the html page for the Tournament Sort page today! Later on will Added the TypeScript, then finish implementing the algorithm in C, C++, Java, and Python."}, {"post_id": 34392, "ts": "2026-07-25T02:28:24Z", "hours": 1.7706, "text": "algora dev log #15 Spent some time building the Tim Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Tournament Sort!"}, {"post_id": 33831, "ts": "2026-07-24T02:12:45Z", "hours": 1.135, "text": "algora dev log #14 Spent some time building the Shell Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Tim Sort!"}, {"post_id": 33801, "ts": "2026-07-24T00:59:13Z", "hours": 1.1319, "text": "algora dev log #13 Spent some time building the Selection Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Shell Sort!"}, {"post_id": 33277, "ts": "2026-07-23T02:07:28Z", "hours": 1.3489, "text": "algora dev log #12 Spent some time building the Radix Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Selection Sort!"}, {"post_id": 33173, "ts": "2026-07-22T20:46:04Z", "hours": 1.49, "text": "algora dev log #11 Spent some time building the Quick Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Radix Sort!"}, {"post_id": 32730, "ts": "2026-07-22T02:16:03Z", "hours": 1.4678, "text": "algora dev log #10 Spent some time building the Merge Sort (in place) page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Quick Sort!"}, {"post_id": 32695, "ts": "2026-07-22T00:35:59Z", "hours": 1.7364, "text": "algora dev log #9 Spent some time building the Merge Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python.Next up: Merge (in place) Sort!"}, {"post_id": 32136, "ts": "2026-07-21T02:57:42Z", "hours": 1.9833, "text": "algora dev log #9 Spent some time building the Intro Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Merge Sort!"}, {"post_id": 32097, "ts": "2026-07-21T00:52:09Z", "hours": 1.195, "text": "algora dev log #8 Spent some time building the Insertion Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Intro Sort!"}, {"post_id": 31515, "ts": "2026-07-19T23:55:04Z", "hours": 1.6153, "text": "algora dev log #7 Spent some time building the Heap Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python.Next up: insertion Sort!"}, {"post_id": 31020, "ts": "2026-07-19T02:32:46Z", "hours": 1.5067, "text": "algora dev log #6 Spent some time building the Counting Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Heap Sort!"}, {"post_id": 30422, "ts": "2026-07-17T23:05:03Z", "hours": 1.6528, "text": "algora dev log #5 Spent some time building the Bucket Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Counting Sort!"}, {"post_id": 30350, "ts": "2026-07-17T20:27:56Z", "hours": 1.4964, "text": "algora dev log #4 Spent some time building the Bubble Sort page today! Added the frontend and TypeScript then finished implementations in C, C++, Java, and Python. Next up: Bucket Sort !"}, {"post_id": 29857, "ts": "2026-07-17T00:36:27Z", "hours": 5.5211, "text": "#Algora dev Log #3 Today’s goal was simple: Get Bitonic Sort fully working from end to end. Turns out that meant building a lot more than just Bitonic Sort. The day started with finishing the Bitonic Sort page itself. The visualizer, controls, explanations, pseudocode section, code viewer, and all the usual algorithm page stuff are now in place. At this point the page finally feels like a real part of the site instead of a placeholder. Most of the work ended up happening behind the scenes. I spent a good chunk of time building out the animation system that powers the visualizer. The goal was to make algorithms describe what they’re doing (compare, swap, finished, etc.) and let the frontend handle the actual animation. It took a bit of trial and error, but the system is finally working and feels flexible enough to support future algorithms. I also ended up writing way more TypeScript than expected. What started as “I just need Bitonic Sort working” somehow turned into multiple files handling animation control, code loading, documentation parsing, syntax highlighting, copy-to-clipboard functionality, and a bunch of other supporting systems. After that came the real test: implementing Bitonic Sort in all four supported languages. Python C++ Java C The actual algorithm wasn’t the hard part. The important part was making sure everything worked together and that the platform could properly support multiple language implementations for the same algorithm. Thankfully, it worked. By the end of the day I had: Finished the Bitonic Sort page Finished the animation engine Added playback controls Built the code loading system Added documentation parsing Implemented Bitonic Sort in C, C++, Python, and Java Connected everything together The biggest win isn’t really Bitonic Sort itself. The biggest win is that the system now works. Future sorting algorithms should be significantly easier to add because most of the annoying infrastructure work is finally out of the way. Current Status Bitonic Sort page complete Animation system working Multi-language code viewer working Bitonic Sort implemented in C, C++, Python, and Java First sorting algorithm fully integrated into Algora Next up: more algorithms !!"}, {"post_id": 29279, "ts": "2026-07-16T02:56:46Z", "hours": 3.0047, "text": "Algora dev log #2! Spent another session focused on building the Sorting Algorithm Index for Algora. The goal was to create a dedicated hub where users can browse algorithms, compare characteristics, and quickly find the right visualization to explore. Most of the work went into designing the overall layout, building algorithm cards, organizing metadata, and creating filtering and discovery systems. A large portion of the work has gone into creating the Sorting Algorithm Index, a central hub where users can browse, discover, and eventually compare different sorting algorithms. Most of this process involved designing and implementing the interface using HTML and CSS, with a focus on creating a clean layout that can scale as more algorithms and features are added. The page now includes searchable algorithm cards, filtering options, learning path recommendations, and categorized algorithm metadata to help users quickly find relevant topics. While the visual design makes up the majority of the implementation, a small amount of TypeScript has also been introduced to support interactive features such as search and filtering. Progress Continued work on the Algora frontend Improved the landing page and overall UI Fixed various styling and layout issues Continued polishing the user experience Next Steps Implement Python versions of all sorting algorithms Implement C++ versions of all sorting algorithms Implement Java versions of all sorting algorithms Implement C versions of all sorting algorithms Connect code implementations to algorithm pages Continue work on visualization and comparison tools Planned Sorting Algorithms Bitonic Sort Bubble Sort Bucket Sort Counting Sort Heap Sort Insertion Sort Intro Sort Merge Sort Merge Sort (In Place) Quick Sort Radix Sort Selection Sort Shell Sort Tim Sort Tournament Sort"}, {"post_id": 29208, "ts": "2026-07-15T23:03:45Z", "hours": 1.8942, "text": "Working on Algora Spent most of today working on the frontend for Algora , a project I’m building to help learn algorithms through interactive visualizations and comparisons. A lot of my time went into building the main landing page and polishing the overall look of the site. Surprisingly, the hardest part wasn’t the algorithms themselves which ofc it was CSS. I ended up spending way longer than expected trying to get layouts, spacing, responsiveness, and animations to behave the way I wanted. At this point my wrist is probably filing complaints against me. Not everything is working yet either. Some parts of the site are still unfinished, and a few features (including parts of the preview and visualization system) are still being actively worked on. There were definitely a lot of moments where something looked fine in my head and then completely broke the second I opened it in the browser. Even though progress felt slower than I expected, it’s starting to come together. The homepage is beginning to feel like something real instead of a collection of random components, and the overall direction of the project is becoming much clearer. Today’s Progress Continued building the Algora frontend Worked on the main landing page Improved layout and styling across multiple sections Fixed various UI issues Continued work on algorithm visualizations Recorded a development timelapse Still a long way to go, but seeing everything slowly come together has been pretty motivating."}]...
|
Title: Algora by @diputs | Stardance
Devlogs:
Wor Title: Algora by @diputs | Stardance
Devlogs:
Working on Algora Spent most of today working on the frontend for Algora , a project I’m building to help learn algorithms through interactive visualizations and comparisons. A lot of my time went into building the main landing page and polishing the overall look of the site. Surprisingly, the hardest part wasn’t the algorithms themselves which ofc it was CSS. I ended up spending way longer than expected trying to get layouts, spacing, responsiveness, and animations to behave the way I wanted. At this point my wrist is probably filing complaints against me. Not everything is working yet either. Some parts of the site are still unfinished, and a few features (including parts of the preview and visualization system) are still being actively worked on. There were definitely a lot of moments where something looked fine in my head and then completely broke the second I opened it in the browser. Even though progress felt slower than I expected, it’s starting to come together. The homepage is beginning to feel like something real instead of a collection of random components, and the overall direction of the project is becoming much clearer. Today’s Progress Continued building the Algora frontend Worked on the main landing page Improved layout and styling across multiple sections Fixed various UI issues Continued work on algorithm visualizations Recorded a development timelapse Still a long way to go, but seeing everything slowly come together has been pretty motivating.
Algora dev log #2! Spent another session focused on building the Sorting Algorithm Index for Algora. The goal was to create a dedicated hub where users can browse algorithms, compare characteristics, and quickly find the right visualization to explore. Most of the work went into designing the overall layout, building algorithm cards, organizing metadata, and creating filtering and discovery systems. A large portion of the work has gone into creating the Sorting Algorithm Index, a central hub where users can browse, discover, and eventually compare different sorting algorithms. Most of this process involved designing and implementing the interface using HTML and CSS, with a focus on creating a clean layout that can scale as more algorithms and features are added. The page now includes searchable algorithm cards, filtering options, learning path recommendations, and categorized algorithm metadata to help users quickly find relevant topics. While the visual design makes up the majority of the implementation, a small amount of TypeScript has also been introduced to support interactive features such as search and filtering. Progress Continued work on the Algora frontend Improved the landing page and overall UI Fixed various styling and layout issues Continued polishing the user experience Next Steps Implement Python versions of all sorting algorithms Implement C++ versions of all sorting algorithms Implement Java versions of all sorting algorithms Implement C versions of all sorting algorithms Connect code implementations to algorithm pages Continue work on visualization and comparison tools Planned Sorting Algorithms Bitonic Sort Bubble Sort Bucket Sort Counting Sort Heap Sort Insertion Sort Intro Sort Merge Sort Merge Sort (In Place) Quick Sort Radix Sort Selection Sort Shell Sort Tim Sort Tournament Sort
#Algora dev Log #3 Today’s goal was simple: Get Bitonic Sort fully working from end to end. Turns out that meant building a lot more than just Bitonic Sort. The day started with finishing the Bitonic Sort page itself. The visualizer, controls, explanations, pseudocode section, code viewer, and all the usual algorithm page stuff are now in place. At this point the page finally feels like a real part of the site instead of a placeholder. Most of the work ended up happening behind the scenes. I spent a good chunk of time building out the animation system that powers the visualizer. The goal was to make algorithms describe what they’re doing (compare, swap, finished, etc.) and let the frontend handle the actual animation. It took a bit of trial and error, but the system is finally working and feels flexible enough to support future algorithms. I also ended up writing way more TypeScript than expected. What started as “I just need Bitonic Sort working” somehow turned into multiple files handling animation control, code loading, documentation parsing, syntax highlighting, copy-to-clipboard functionality, and a bunch of other supporting systems. After that came the real test: implementing Bitonic Sort in all four supported languages. Python C++ Java C The actual algorithm wasn’t the hard part. The important part was making sure everything worked together and that the platform could properly support multiple language implementations for the same algorithm. Thankfully, it worked. By the end of the day I had: Finished the Bitonic Sort page Finished the animation engine Added playback controls Built the code loading system Added documentation parsing Implemented Bitonic Sort in C, C++, Python, and Java Connected everything together The biggest win isn’t really Bitonic Sort itself. The biggest win is that the system now works. Future sorting algorithms should be significantly easier to add because most of the annoying infrastructure work is finally out of the way. Current Status Bitonic Sort page complete Animation system working Multi-language code viewer working Bitonic Sort implemented in C, C++, Python, and Java First sorting algorithm fully integrated into Algora Next up: more algorithms !!
algora dev log #4 Spent some time building the Bubble Sort page today! Added the frontend and TypeScript then finished implementations in C, C++, Java, and Python. Next up: Bucket Sort !
algora dev log #5 Spent some time building the Bucket Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Counting Sort!
algora dev log #6 Spent some time building the Counting Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Heap Sort!
algora dev log #7 Spent some time building the Heap Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python.Next up: insertion Sort!
algora dev log #8 Spent some time building the Insertion Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Intro Sort!
algora dev log #9 Spent some time building the Intro Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Merge Sort!
algora dev log #9 Spent some time building the Merge Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python.Next up: Merge (in place) Sort!
algora dev log #10 Spent some time building the Merge Sort (in place) page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Quick Sort!
algora dev log #11 Spent some time building the Quick Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Radix Sort!
algora dev log #12 Spent some time building the Radix Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Selection Sort!
algora dev log #13 Spent some time building the Selection Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Shell Sort!
algora dev log #14 Spent some time building the Shell Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Tim Sort!
algora dev log #15 Spent some time building the Tim Sort page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Tournament Sort!
algora dev log #16 don’t have much time today but build the html page for the Tournament Sort page today! Later on will Added the TypeScript, then finish implementing the algorithm in C, C++, Java, and Python.
algora dev log #17 Finished Tournament Sort page today! Added the TypeScript, then finish implementing the algorithm in C, C++, Java, and Python.
algora dev log #18 Worked on the new algorithm comparison page. You can now compare two sorting algorithms side by side, view their information, watch the visualizations together, and browse the implementations in C, C++, Java, and Python. This ended up being one of the larger frontend pages in Algora so far and took a long time to write the typescript engine.
algora dev log #19 Adding the main page for searching algorithms layout, finishing the HTML and TypeScript for the page
algora dev log #20 Adding the missing main page TypeScript, now the animation is working in the main page.
algora dev log #21 Spent some time building the Binary Search page today! Added the frontend and TypeScript. will finished the python, c, hava, and c++ implementation later!
algora dev log #22 Spent some time building the Bitonic Array Search page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Exponential Search!
algora dev log #22 Spent some time building the Exponential Search page today! Added the frontend and TypeScript, then finished implementations in C, C++, Java, and Python. Next up: Fibonacci Search!
algora dev log #23 Changed my mind on this one. I want each algorithm page to include a production style implementation so readers can compare a simplified, educational version with a more advanced version that’s closer to what you might actually see in production. Because of that, I went back and started adding Bitonic Sort implementations in all four supported languages.
algora dev log #24 Spent some time implementing the advanced version of Bubble Sort today. Finished implementations in C, C++, Java, and Python.
algora dev log #25 Spent some time implementing the advanced version of Bucket Sort today. Finished implementations in C, C++, Java, and Python.
algora dev log #26 Spent some time implementing the advanced version of Counting Sort today. Finished implementations in C, C++, Java, and Python.
#algora dev log #27 Spent some time implementing the advanced version of Heap Sort today. Finished implementations in C, C++, Java, and Python.
algora dev log #28 Spent some time implementing the advanced version of Insertion and Intro Sort today. Finished implementations in C, C++, Java, and Python.
algora dev log #29 Spent some time implementing the advanced version of Merge Sort today. Finished implementations in C, C++, Java, and Python.
algora dev log #30 Spent some time implementing the advanced version of Merge Sort (in place) today. Finished implementations in C, C++, Java, and Python. honestly this one is pretty hard to get it right
algora dev log #31 Spent some time implementing the advanced version of Quick Sort . Finished implementations in C, C++, Java, and Python.
algora dev log #32 Spent some time implementing the advanced version of Radix Sort . Finished implementations in C, C++, Java, and Python.
algora dev log #33 Spent some time implementing the advanced version of Selection Sort . Finished implementations in C, C++, Java, and Python.
algora dev log #34 Spent some time implementing the advanced version of Shell Sort . Finished implementations in C, C++, Java, and Python.
algora dev log #35 Spent some time implementing the advanced version of Tim Sort . Finished implementations in C, C++, Java, and Python.
algora dev log #36 Spent some time implementing the advanced version of Tournament Sort . Finished implementations in C, C++, Java, and Python.
algora dev log #37 Spent some time implementing the advanced version of Binary Search . Finished implementations in C, C++, Java, and Python.
algora dev log #38 Spent some time implementing the advanced version of Bitonic Array Search. Finished implementations in C, C++, Java, and Python.
algora dev log #39 Spent some time implementing the advanced version of Exponential Search. Finished implementations in C, C++, Java, and Python.
algora dev log #40 Implemented the page for Fibonacci Search. Finished both easy and advanced version of Fibonacci search implementations in C, C++, Java, and Python. Together with the engine
algora dev log #41 Implemented the page for Galloping Search. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #42 Implemented the page for Interpolation Search. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #43 Thought the interpolation search lab was done until I actually used it. It turned out it showed the result, but not the reasoning. Added a new visualization that explains how the probe is estimated instead of just where it lands.
algora dev log #44 Implemented the page for Jump Search. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #45 Implemented the page for Linear Search. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #46 Implemented the page for Meta Binary Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python.
algora dev log #47 Implemented the page for Sentinel Linear Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python.
algora dev log #48 Implemented the page for Ternary Linear Search. Finished both easy and advanced versions, Finished implementations in C, C++, Java, and Python.
algora dev log #49 Implemented the page for Uniform Binary Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python.
algora dev log #50 Finishes and upgrade the page for Bitonic Array Search, added custom workspace for the search
algora dev log #51 Implemented the page for Fractional Cascading. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python.
algora dev log #52 Implemented the page for Hash Table Lookup. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python.
algora dev log #53 Implemented the page for Parametric Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python.
algora dev log #54 Implemented the page for Quickselect. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python.
algora dev log #55 Implemented the page for Rotated Sorted Array Search. Finished both easy and advanced versions, Made it’s own custom workspace. Finished implementations in C, C++, Java, and Python.
algora dev log #55 Upgraded the Exponential Search, Galloping Search, and Jump Search workspace so they are more intuitive. :D and the most time consuming part of upgrading the css
#algora dev log # 57 Finally finished the Algorithm Comparison page for searching algorithm class, Compelted the HTML and TypeScript implementations. Added some details like warnings. Finished with the Search Class of Algorithms 🎉
#algora dev log #58 Update Readme for the shipping :D The Making of Algora Below is a 1000× speed timelapse of me building the project. I lost some footage along the way, so this isn’t every single thing that happened, but most of the development is here. Altogether, it represents roughly 70 hours of raw development footage condensed into this video. It is kind of crazy to look back at all of that work and see it compressed into a few minutes.
Algora dev log #59 Apparently shipping 1.0 means I can add more things now. Started building out graph algorithms, new index, filters, learning paths, complexity info, and the foundation for the graph workspaces.
algora dev log # 60 Built the reusable grid search engine for graph/search algorithms. Finished DFS as the first full test of the engine traversal, animation, visited states, and path reconstruction all working. Added randomized maze generation with start/target. Built and integrated the full DFS algorithm page + interactive workspace. Engine is now ready to reuse for the rest of the search algorithms
algora dev log # 61 Finished both standard and advanced DFS implementations. Standard version keeps the algorithm clean. Advanced version adds a more optimized/practical implementation with deeper control over traversal
algora dev log #62 Implemented the page for 0-1 BFS. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #63 Implemented the page for A* pathfinding. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #64 Implemented the page for Bellman-Ford. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #65 Implemented the page for BFS. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #66 Implemented the page for Bidirectional BFS. Finished both easy and advanced versions, finished implementations in C, C++, Java, and Python.
algora dev log #67 Implemented the page for D* Lite . Finished the algorithm workspace, this one is hard and the TypeScript is behaving weirdly, Gotta rewrite a lot of times for the engine to render correctly with the different heuristic implemented and clicking to change the board while the algorithm runs But it works now. Really proud of it.
algora dev log #68 Implemented Python C++ Java and C for D* Lite. Both the advanced and teaching implementation is finished.
I got my latest project reviews back today, and first of all thank you so much to everyone who took the time to look through the project! A lot of the feedback genuinely made me really happy. Some people pointed out bugs I hadn’t noticed, some gave me ideas for things I could improve, and a few of you even said you wanted to bookmark the project or use it for studying algorithms, which honestly means a lot to me. There were also some questions about the tech stack and how different parts of the project were built, which I’m happy to answer. Review #1: “what is this stack? html, typescript, c, c++ and java?” The website itself is built primarily with HTML, CSS, and TypeScript. The additional languages: C, C++, Java, and Python are there because the project includes implementations of the algorithms in multiple languages. Review #5: “Dijkstra doesn’t work, I get a 404” The Ship is mainly about sorting and searching algorithms. I think by the time you visited the pathfinding page, I had already started working on the new pathfinding section but hadn’t finished implementing it yet, which is why Dijkstra gave you a 404. I’m actively working on the Dijkstra right now tho! Review #8: “the amount of time to type out your project at 70 words per minute is around 200 hours” This assumes every character was typed manually. I use tab completion and copy/paste repetitive code. For example, most of the TypeScript engine API is basically the same across algorithms, so I’m obviously not going to retype the same thing every time Review #8: “make no mention of struggles or frustrations, inspirations or ideas” I personally don’t like putting my frustration into project logs. I usually just keep my devlogs focused on what I made, changed, or learned. Review #8: “You type out rgba and hexadecimals by hand without a tool” I sketched the website layout on my iPad and used it as a reference while coding. I can’t exactly copy/paste an RGB value from my iPad onto my computer, so I just typed it Review #8: “you type out entire svg lines” The SVGs are actually copy/pasted. They’re reused assets and basically the same every time. The timelapse is around 1000x speed, so a paste just looks like the SVG suddenly appeared. Review #8: “You never delete any lines in your timelapses” I cut most of the boring debugging out because who wants to watch that. That’s also why sometimes the timelapse jumps from broken code to fixed code. I wanted the videos to stay short and watchable. Review #8: “Show us your second monitor” There is no second monitor. You assumed there was one because I wasn’t recording my full screen. I intentionally only record the part of my screen relevant to the project because the rest is my privacy. I also look through a lot of references, documentation, and other pages while working, and I don’t think anyone wants all of that flashing across a 1000x timelapse anyway. I don’t mind people questioning my work or asking how I did something, but I do think there’s a difference between asking and immediately assuming someone faked their work. Some of the comments also became pretty personal, which was honestly disappointing to see. Review #9: “Don’t bring it down pls” don’t worry, I’m planning to keep the website up I’m really happy that you actually want to use it though, that’s basically the whole reason I made this project And to everyone else who left a review, thank you so much!! I didn’t want to respond to every review with just “thank you” over and over again, but I read all of them and really appreciate all the feedback Seeing people say they actually want to use the website for learning, revision, or even USACO genuinely made me really happy. That’s basically why I wanted to build this project in the first place
algora dev log #68 Implemented the page for Dijkstra. Finished the algorithm workspace, Both the advanced and teaching version of the algorithm in all 4 languages including python, c, java and c++
Ships:
Algora 1.0 Ship An attempt to put everything I wish I had when learning algorithms into one place. This project started when a friend asked me for help with USACO. While helping them, I started thinking back to my own path of learning algorithms and the things I wished I had when I was learning them. I wanted one place where I could not only read about an algorithm, but actually see it work, understand what is happening at each step, compare it with other algorithms, and look at both a teaching implementation and a more practical implementation. That idea eventually became Algora. What’s Included The first release currently includes: 15 sorting algorithms 17 searching algorithms Interactive algorithm visualizations Step by step execution and playback controls Algorithm specific workspaces for more complex algorithms Quick facts and complexity information Detailed algorithm explanations Pseudocode Teaching implementations in C, C++, Java, and Python Production style implementations in C, C++, Java, and Python Sorting algorithm comparison Searching algorithm comparison Comparison information including time complexity, space complexity, requirements, strengths, and weaknesses Warnings when algorithms are not directly comparable Individual pages for every algorithm The goal was to make each algorithm more than just a page of code. I wanted to be able to go from: What is this? → How does it work? → Can I see it happen? → Can I understand each step? → What does the code look like? → How does it compare to other approaches? Why I Built It I built Algora because I kept thinking about how much easier learning algorithms could have been if I had all of these things in one place when I was going through USACO. I’ve gone through the USACO training path myself and reached Platinum, and helping my friend brought me back to the way I learned all of this in the first place. So instead of making another collection of algorithm implementations, I wanted to make the resource I wish I had back then. Algora is an attempt to put everything I wish I had when learning algorithms into one place. The Making of Algora I also recorded a large part of the development process while building the project. The video below is a 1000× speed timelapse made from roughly 70 hours of raw development footage. I lost some footage along the way, so it does not contain absolutely everything, but most of the development process is there. Apparently Stands saw the size of the video and decided it simply did not exist D: If you really want to watch the video, the link is here: https://github.com/diputs-sudo/algora/tree/main/video you might have to download it :( Thank You Thank you to everyone who supported me while I was building Algora. Thank you to everyone who tested things, gave feedback, helped me figure things out, listened to me ramble about algorithms, or simply encouraged me to keep working on it. This project ended up becoming much bigger than the original idea, and I’m really happy that I finally get to share it....
|
1792878745.069722
|
1793742745.069722
|
|
51767
|
Friction less by @jinjin | Stardance
|
jinjin
|
1
|
128.53
|
110
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-08-31T12:30:11Z", "hours": 39.0, "multiplier": 3.55, "payout": 110, "blessing": "neutral", "text": "I built TidyPaste, a client-side web workbench designed to clean messy text, reformat data structures, and track session productivity - all inside a single browser tab.", "repo": "https://github.com/jinjintt89-cmyk/Friction-less-copy-and-paste", "demo": "https://copyandpaste2.vercel.app/"}]...
|
[{"post_id": 65982, "ts": "202 [{"post_id": 65982, "ts": "2026-09-11T14:14:33Z", "hours": 4.6333, "text": "Polishing the Desktop VersionI’ve continued working on the desktop version of Frictionless and focused on making it feel more like a proper standalone application.A big part of this work has been testing the Mac build, fixing issues that appeared during packaging, and making sure the desktop version still has the same core functionality as the web version.I also spent time improving the overall reliability of the project after moving more of the code into the Electron setup. This included checking that the app opens correctly, that the different tools continue to work, and that the project can still be built and packaged when changes are made.The desktop version is getting much closer to feeling like a complete second version of Frictionless rather than just a website running inside a window.What I worked onTested the Mac desktop buildImproved Electron integrationChecked existing Frictionless toolsFixed packaging-related issuesImproved desktop reliabilityContinued preparing the app for distributionKept the web and desktop versions working together"}, {"post_id": 65868, "ts": "2026-09-11T09:28:32Z", "hours": 9.1667, "text": "Fixing the Mac App and Improving DistributionI’ve been continuing work on the desktop version of Frictionless and focusing on making the Mac app easier to distribute.After successfully building the Electron version, I ran into an issue where macOS showed a “damaged” message when the app was downloaded. I investigated the problem and found that it was related to macOS security and the app being unsigned.I’ve now been working on ad-hoc signing the application and testing the build again. This has been useful because it showed me that building an application and actually distributing it are two different challenges.I also made sure the desktop-app changes were properly pushed to the Frictionless GitHub repository, so the project now contains both the original web version and the Electron desktop version.The main focus right now is making the desktop version as reliable as the web version while keeping the project simple and easy to access.What I worked onInvestigated the macOS “damaged app” issueTested ad-hoc code signingContinued improving the Electron buildPushed the desktop-app code to GitHubWorked on making the Mac version easier to distributeKept the web version available as an alternative"}, {"post_id": 65613, "ts": "2026-09-10T21:07:53Z", "hours": 9.8333, "text": "Making Frictionless a Desktop AppI’ve been working on turning Frictionless into more than just a web app by creating a proper desktop version for macOS.I set up Electron and Electron Forge to package the existing Frictionless code into a standalone Mac application. The app now runs in its own window and keeps the same tools and features from the web version.I also configured the project for Apple Silicon Macs and successfully built the application into a distributable .zip file. I connected all of the new Electron files to the GitHub repository as well, so the desktop version is now part of the main project.One issue I ran into was macOS showing a “damaged” warning when opening the downloaded application. After investigating, I found that this is related to macOS security checks for unsigned applications rather than the app itself being broken. I’m now testing ad-hoc signing while keeping the web version available as the easiest way to use Frictionless.This was a big milestone for the project because Frictionless can now work as both a web application and a standalone desktop application.What I worked onAdded ElectronAdded Electron ForgeCreated the desktop app windowBuilt an Apple Silicon Mac versionCreated a distributable ZIPAdded the desktop files to GitHubTested macOS app signingKept the web version working alongside the desktop version"}, {"post_id": 65305, "ts": "2026-09-10T11:16:42Z", "hours": 8.9792, "text": "Turning Frictionless Into a Desktop AppI’ve been working on taking Frictionless beyond just being a web app and turning it into a proper desktop application.I used Electron to package the existing HTML, CSS, and JavaScript version into a native-style macOS app. This means Frictionless can now run in its own window instead of requiring a browser tab.I also set up Electron Forge so the project can be packaged into a distributable Mac application. The app successfully builds for Apple Silicon, which means it works with Macs using Apple Silicon processors.I connected the desktop version back to the same GitHub repository as the web version, so both versions can continue to be developed together.I also spent some time dealing with macOS security and app signing. The app itself was working, but macOS can show a “damaged” warning for unsigned applications downloaded from the internet. I’m now testing free ad-hoc signing for local use while keeping the web version available as the easiest way to try Frictionless.This is a pretty big step for the project because Frictionless is no longer just a simple website. It can now exist as both a web tool and a standalone desktop application.What I addedElectron desktop appmacOS Apple Silicon buildElectron Forge packagingDesktop app entry pointGitHub integration for the desktop sourcemacOS signing testingKept the original web version workingThe goal is to make Frictionless feel like a real, complete tool that can be used wherever it is most convenient."}, {"post_id": 65091, "ts": "2026-09-09T23:47:56Z", "hours": 8.4333, "text": "Connecting Everything TogetherToday I spent around 8 hours and 50 minutes working on Frictionless and connecting the new tools together more smoothly.With the number of features growing, I focused on making sure everything still feels like one application instead of a collection of separate tools. I cleaned up interactions between the different text tools, improved how results are displayed, and fixed smaller issues I found while testing everything.I also spent time testing different types of input, including messy text, repeated lines, formatted data, and larger pieces of content. This helped me find and fix a few edge cases that weren’t obvious when testing with simple examples.The project is getting much closer to the version I want to ship. At this point I’m focusing less on just adding features and more on making sure the features I already built actually feel polished and useful."}, {"post_id": 64807, "ts": "2026-09-09T14:14:11Z", "hours": 5.6, "text": "Expanding FrictionlessToday I worked on expanding Frictionless from a simple text-cleaning tool into a much more complete text and data workbench.I added a bunch of new tools for different situations, including duplicate line removal, sorting, case conversion, text statistics, JSON formatting and validation, CSV tools, Markdown tools, and Diff/Compare.I also worked on features like Undo/Redo and Saved Presets so users can perform multiple transformations without worrying about losing their original text, and save workflows they use regularly.The main goal with these changes was to make Frictionless something that could actually be useful in everyday projects instead of only being a basic copy-and-paste cleaner.There is still plenty I want to polish, but the project is starting to feel much more like a complete productivity tool."}, {"post_id": 64679, "ts": "2026-09-09T08:36:55Z", "hours": 8.3781, "text": "Today I worked on adding a proper Find & Replace feature to Frictionless.The goal was to make it easier to quickly change specific words or phrases without having to manually search through a large amount of text. I added separate find and replace inputs, along with options that give more control over how searches work.I also made sure the feature fits into the existing workflow instead of feeling like a separate tool. Users can search through their text, see how many matches were found, and replace individual matches or all matching text at once.This is one of the bigger improvements I’ve made recently because it makes Frictionless more useful for actually working with messy or repetitive text, rather than only cleaning and converting it."}, {"post_id": 64458, "ts": "2026-09-08T21:13:08Z", "hours": 11.2139, "text": "Today I spent some time improving a few smaller parts of Frictionless that aren’t necessarily big features, but make the app nicer to use.I adjusted some of the interactions around the text tools so they give clearer feedback when an action is completed. I also cleaned up a few small inconsistencies between different buttons and controls.These changes were mostly about the little details. Frictionless has quite a few different tools now, so making sure everything behaves consistently is becoming more important as the project grows.Nothing huge was added today, but the overall experience feels a bit more polished and predictable."}, {"post_id": 64116, "ts": "2026-09-08T09:52:57Z", "hours": 4.1708, "text": "Timer & Calendar Improvements Today I worked on two parts of Frictionless: the timer and the calendar. I added a timer so I can keep track of how long I’m spending on a task while working in the app. I also went through the calendar and fixed several issues with how it behaved and displayed information. The calendar needed some extra attention because small problems with dates and interactions made it less reliable to use. I cleaned those areas up and made the experience smoother. These features aren’t the main text-cleaning tools, but they fit the idea of Frictionless being a small productivity workbench rather than just a place to paste and format text. There are still some improvements I’d like to make, but both features are now working much more reliably."}, {"post_id": 63902, "ts": "2026-09-07T22:47:41Z", "hours": 10.9, "text": "Timer & Calendar Improvements Today I added a timer to the project to make it more useful and interactive. I also spent some time fixing issues with the calendar and making sure it works properly. The timer should make it easier to keep track of time while using the app, while the calendar fixes should make scheduling and viewing events smoother. There are still a few things I want to improve, but the core features are now working much better."}, {"post_id": 63495, "ts": "2026-09-07T11:51:38Z", "hours": 6.5144, "text": "Background & Calendar Improvements Today I added a green background to give the UI a cleaner and more consistent look. I also continued working on the calendar, making improvements to how it looks and functions. These changes help make the project feel more polished and easier to use. I’m happy with how the new background fits with the rest of the interface, and I’ll keep improving the calendar as I work on the project."}, {"post_id": 61875, "ts": "2026-09-05T01:06:44Z", "hours": 1.7117, "text": "Code Improvements Today I worked on improving and cleaning up the code to make it more organised and easier to understand. I went through different parts of the project, made some sections simpler, and fixed small issues along the way. The goal was to make the code feel more natural and readable while keeping everything working properly. This should also make it easier to continue adding features and fixing bugs in the future."}, {"post_id": 52825, "ts": "2026-08-23T05:39:34Z", "hours": 5.8, "text": "Today I spent a long session finishing up the main parts of Frictionless. I worked through the remaining code, fixed issues as I found them, and made sure the different parts of the project were working together properly. A lot of the work was focused on getting everything into a usable state rather than adding one specific feature. I went back through parts that weren’t quite working as expected and cleaned them up. This was a big step toward getting the project ready to use properly, and it gave me a much better base to build the next features on."}, {"post_id": 52691, "ts": "2026-08-22T23:34:53Z", "hours": 12.1053, "text": "Today I continued working on the main code for Frictionless. I ran into an error that stopped part of the project from working correctly, so I spent time tracking down what was causing it and fixing the problem. After getting it working again, I tested the affected parts to make sure the fix didn’t break anything else. I also made a few small adjustments while going through the code. This was mainly a debugging session, but fixing these problems helped make the project more stable and gave me a better understanding of how the different parts of the app connect."}, {"post_id": 52254, "ts": "2026-08-22T10:59:09Z", "hours": 5.1794, "text": "Today I continued building Frictionless and spent most of the session working through the code and debugging issues as they came up. I tested different parts of the project while developing and fixed problems that were preventing certain features from working properly. I also made small changes to improve how the different parts of the app worked together. Most of the progress today came from fixing problems rather than adding a single large feature. Each issue I solved made the project more stable and brought it closer to being a complete, usable workbench."}, {"post_id": 52008, "ts": "2026-08-22T01:13:23Z", "hours": 15.5908, "text": "Today I continued building the core of Frictionless, focusing on writing and connecting the main parts of the application. I worked through the structure of the project and started putting the different features together so they could work as one consistent experience. Along the way, I tested what I had built and made adjustments where things weren’t working as expected. At this stage, I was mainly focused on getting a solid foundation in place. This gave me something reliable to build on as I started adding more features and improving the overall UI."}]...
|
Title: Friction less by @jinjin | Stardance
Devlo Title: Friction less by @jinjin | Stardance
Devlogs:
Today I continued building the core of Frictionless, focusing on writing and connecting the main parts of the application. I worked through the structure of the project and started putting the different features together so they could work as one consistent experience. Along the way, I tested what I had built and made adjustments where things weren’t working as expected. At this stage, I was mainly focused on getting a solid foundation in place. This gave me something reliable to build on as I started adding more features and improving the overall UI.
Today I continued building Frictionless and spent most of the session working through the code and debugging issues as they came up. I tested different parts of the project while developing and fixed problems that were preventing certain features from working properly. I also made small changes to improve how the different parts of the app worked together. Most of the progress today came from fixing problems rather than adding a single large feature. Each issue I solved made the project more stable and brought it closer to being a complete, usable workbench.
Today I continued working on the main code for Frictionless. I ran into an error that stopped part of the project from working correctly, so I spent time tracking down what was causing it and fixing the problem. After getting it working again, I tested the affected parts to make sure the fix didn’t break anything else. I also made a few small adjustments while going through the code. This was mainly a debugging session, but fixing these problems helped make the project more stable and gave me a better understanding of how the different parts of the app connect.
Today I spent a long session finishing up the main parts of Frictionless. I worked through the remaining code, fixed issues as I found them, and made sure the different parts of the project were working together properly. A lot of the work was focused on getting everything into a usable state rather than adding one specific feature. I went back through parts that weren’t quite working as expected and cleaned them up. This was a big step toward getting the project ready to use properly, and it gave me a much better base to build the next features on.
Code Improvements Today I worked on improving and cleaning up the code to make it more organised and easier to understand. I went through different parts of the project, made some sections simpler, and fixed small issues along the way. The goal was to make the code feel more natural and readable while keeping everything working properly. This should also make it easier to continue adding features and fixing bugs in the future.
Background & Calendar Improvements Today I added a green background to give the UI a cleaner and more consistent look. I also continued working on the calendar, making improvements to how it looks and functions. These changes help make the project feel more polished and easier to use. I’m happy with how the new background fits with the rest of the interface, and I’ll keep improving the calendar as I work on the project.
Timer & Calendar Improvements Today I added a timer to the project to make it more useful and interactive. I also spent some time fixing issues with the calendar and making sure it works properly. The timer should make it easier to keep track of time while using the app, while the calendar fixes should make scheduling and viewing events smoother. There are still a few things I want to improve, but the core features are now working much better.
Timer & Calendar Improvements Today I worked on two parts of Frictionless: the timer and the calendar. I added a timer so I can keep track of how long I’m spending on a task while working in the app. I also went through the calendar and fixed several issues with how it behaved and displayed information. The calendar needed some extra attention because small problems with dates and interactions made it less reliable to use. I cleaned those areas up and made the experience smoother. These features aren’t the main text-cleaning tools, but they fit the idea of Frictionless being a small productivity workbench rather than just a place to paste and format text. There are still some improvements I’d like to make, but both features are now working much more reliably.
Today I spent some time improving a few smaller parts of Frictionless that aren’t necessarily big features, but make the app nicer to use.I adjusted some of the interactions around the text tools so they give clearer feedback when an action is completed. I also cleaned up a few small inconsistencies between different buttons and controls.These changes were mostly about the little details. Frictionless has quite a few different tools now, so making sure everything behaves consistently is becoming more important as the project grows.Nothing huge was added today, but the overall experience feels a bit more polished and predictable.
Today I worked on adding a proper Find & Replace feature to Frictionless.The goal was to make it easier to quickly change specific words or phrases without having to manually search through a large amount of text. I added separate find and replace inputs, along with options that give more control over how searches work.I also made sure the feature fits into the existing workflow instead of feeling like a separate tool. Users can search through their text, see how many matches were found, and replace individual matches or all matching text at once.This is one of the bigger improvements I’ve made recently because it makes Frictionless more useful for actually working with messy or repetitive text, rather than only cleaning and converting it.
Expanding FrictionlessToday I worked on expanding Frictionless from a simple text-cleaning tool into a much more complete text and data workbench.I added a bunch of new tools for different situations, including duplicate line removal, sorting, case conversion, text statistics, JSON formatting and validation, CSV tools, Markdown tools, and Diff/Compare.I also worked on features like Undo/Redo and Saved Presets so users can perform multiple transformations without worrying about losing their original text, and save workflows they use regularly.The main goal with these changes was to make Frictionless something that could actually be useful in everyday projects instead of only being a basic copy-and-paste cleaner.There is still plenty I want to polish, but the project is starting to feel much more like a complete productivity tool.
Connecting Everything TogetherToday I spent around 8 hours and 50 minutes working on Frictionless and connecting the new tools together more smoothly.With the number of features growing, I focused on making sure everything still feels like one application instead of a collection of separate tools. I cleaned up interactions between the different text tools, improved how results are displayed, and fixed smaller issues I found while testing everything.I also spent time testing different types of input, including messy text, repeated lines, formatted data, and larger pieces of content. This helped me find and fix a few edge cases that weren’t obvious when testing with simple examples.The project is getting much closer to the version I want to ship. At this point I’m focusing less on just adding features and more on making sure the features I already built actually feel polished and useful.
Turning Frictionless Into a Desktop AppI’ve been working on taking Frictionless beyond just being a web app and turning it into a proper desktop application.I used Electron to package the existing HTML, CSS, and JavaScript version into a native-style macOS app. This means Frictionless can now run in its own window instead of requiring a browser tab.I also set up Electron Forge so the project can be packaged into a distributable Mac application. The app successfully builds for Apple Silicon, which means it works with Macs using Apple Silicon processors.I connected the desktop version back to the same GitHub repository as the web version, so both versions can continue to be developed together.I also spent some time dealing with macOS security and app signing. The app itself was working, but macOS can show a “damaged” warning for unsigned applications downloaded from the internet. I’m now testing free ad-hoc signing for local use while keeping the web version available as the easiest way to try Frictionless.This is a pretty big step for the project because Frictionless is no longer just a simple website. It can now exist as both a web tool and a standalone desktop application.What I addedElectron desktop appmacOS Apple Silicon buildElectron Forge packagingDesktop app entry pointGitHub integration for the desktop sourcemacOS signing testingKept the original web version workingThe goal is to make Frictionless feel like a real, complete tool that can be used wherever it is most convenient.
Making Frictionless a Desktop AppI’ve been working on turning Frictionless into more than just a web app by creating a proper desktop version for macOS.I set up Electron and Electron Forge to package the existing Frictionless code into a standalone Mac application. The app now runs in its own window and keeps the same tools and features from the web version.I also configured the project for Apple Silicon Macs and successfully built the application into a distributable .zip file. I connected all of the new Electron files to the GitHub repository as well, so the desktop version is now part of the main project.One issue I ran into was macOS showing a “damaged” warning when opening the downloaded application. After investigating, I found that this is related to macOS security checks for unsigned applications rather than the app itself being broken. I’m now testing ad-hoc signing while keeping the web version available as the easiest way to use Frictionless.This was a big milestone for the project because Frictionless can now work as both a web application and a standalone desktop application.What I worked onAdded ElectronAdded Electron ForgeCreated the desktop app windowBuilt an Apple Silicon Mac versionCreated a distributable ZIPAdded the desktop files to GitHubTested macOS app signingKept the web version working alongside the desktop version
Fixing the Mac App and Improving DistributionI’ve been continuing work on the desktop version of Frictionless and focusing on making the Mac app easier to distribute.After successfully building the Electron version, I ran into an issue where macOS showed a “damaged” message when the app was downloaded. I investigated the problem and found that it was related to macOS security and the app being unsigned.I’ve now been working on ad-hoc signing the application and testing the build again. This has been useful because it showed me that building an application and actually distributing it are two different challenges.I also made sure the desktop-app changes were properly pushed to the Frictionless GitHub repository, so the project now contains both the original web version and the Electron desktop version.The main focus right now is making the desktop version as reliable as the web version while keeping the project simple and easy to access.What I worked onInvestigated the macOS “damaged app” issueTested ad-hoc code signingContinued improving the Electron buildPushed the desktop-app code to GitHubWorked on making the Mac version easier to distributeKept the web version available as an alternative
Polishing the Desktop VersionI’ve continued working on the desktop version of Frictionless and focused on making it feel more like a proper standalone application.A big part of this work has been testing the Mac build, fixing issues that appeared during packaging, and making sure the desktop version still has the same core functionality as the web version.I also spent time improving the overall reliability of the project after moving more of the code into the Electron setup. This included checking that the app opens correctly, that the different tools continue to work, and that the project can still be built and packaged when changes are made.The desktop version is getting much closer to feeling like a complete second version of Frictionless rather than just a website running inside a window.What I worked onTested the Mac desktop buildImproved Electron integrationChecked existing Frictionless toolsFixed packaging-related issuesImproved desktop reliabilityContinued preparing the app for distributionKept the web and desktop versions working together
Ships:
I built TidyPaste, a client-side web workbench designed to clean messy text, reformat data structures, and track session productivity - all inside a single browser tab....
|
1792878589.8854256
|
1793742589.8854256
|
|
50858
|
OpenDAW by @saisrihari | Stardance
|
saisrihari
|
0
|
128.54
|
0
|
[]
|
[{"post_id": 63592, "ts": "202 [{"post_id": 63592, "ts": "2026-09-07T14:48:48Z", "hours": 7.7333, "text": "I’VE ADDED TWO HUGE FEATURES!!!: 1)Full Mixer- So now we’ve got a dedicated mixer screen with volume faders, pan, mute/solo and a master channel. The colour of the tracks correspond with the mixer track colours. 2)Fixed Timeline Zoom- The zoom is now visible on the left panel. It changes the timeline width. The zoom is also saved with projects."}, {"post_id": 62627, "ts": "2026-09-06T02:58:38Z", "hours": 8.6619, "text": "I’ve fixed some major bugs with the drum machine. I added a startup window which appears when the application is opened. It appears with entries where you can set your project name, BPM, Make a new project, Open an existing project or even settings. I made a new logo which is displayed at the top. I’ve also added a zoom in timeline feature for better viewing in the timeline."}, {"post_id": 62171, "ts": "2026-09-05T13:08:13Z", "hours": 6.3, "text": "I’ve added a settings window. You can now select your preferred microphone, MIDI and you can refresh devices if you’ve connected a new one. Also the keyboard shortcut list is displayed here. Settings is accessible from both the toolbar or the File dropdown"}, {"post_id": 61937, "ts": "2026-09-05T03:36:26Z", "hours": 5.3333, "text": "I removed the fullscreen arrangement feature cause it kept bugging out and I wasn’t able to fix it. I’ll probably add it back on a future date. I also improved the vocals. You can now select your microphone from the input dropdown. You can record vocals when the instrument is playing. I’ve also added a vocal preset to the fx rack. The project is near completion (I’ll say around 3/4th way through)."}, {"post_id": 60964, "ts": "2026-09-03T17:23:11Z", "hours": 5.7111, "text": "I added a fullscreen arangement view. When you click f11 or the arrangement button in the top the daw expands into the timeline. It’s not perfect yet and it’s very buggy so I might remove it later (or fix it). The feature is inspired from ableton live."}, {"post_id": 59690, "ts": "2026-09-01T16:44:27Z", "hours": 8.5333, "text": "I added markers (they get placed at your playheads position). I added Auto-save which is togglable currently but I’ll probably change that soon. I added clip fades which can be set after clicking the fade in or fade out button. A dialogue box appears asking you at what time you need the track to fade in and out. I also expanded the undo/redo to 100 states which now includes fades and the markers. The project is close to completion!"}, {"post_id": 59000, "ts": "2026-08-31T16:06:50Z", "hours": 8.3353, "text": "I added a drum beat maker ! (It shows up with an error tho whenever I try to play more than two drum tracks). It has a 16 step sequencer, A kick, snare, Hi-hats, clap. You can add cells by clicking them (they’re togglable). It uses your current BPM and is added to the timeline using the “Add to timeline” button. The code works by having separate parts for the tracks, audio engine, and GUI, which work together to load."}, {"post_id": 57853, "ts": "2026-08-30T05:04:15Z", "hours": 7.1, "text": "So I added undo and redo buttons (finally) in the top toolbar you can use ctrl+z and ctrl+y as shortcut keys. I’ve also added a looping feature. You can now enable the loop from the on/off button on the transport bar. Set A sets the loop start at current playhead. Set B sets the loop end at current playhead. “L” toggles looping and playback cycles continuously between Set A and Set B."}, {"post_id": 56752, "ts": "2026-08-28T17:15:33Z", "hours": 8.2692, "text": "I finally added an effects rack. The effects I included were: Gain, Delay, Delay Time, Reverb, Distortion, Compressor, High-pass and low-pass filters. Each track now has an “fx” button or you can use the open effects rack on the left panel. The effects are added both to playback and export and you can use the “f” key to open the rack."}, {"post_id": 55580, "ts": "2026-08-27T15:04:58Z", "hours": 8.3667, "text": "I finally found a purpose for the panel on the left. You can now see the current bpm, track count, track length, selected track, position and length. You can also rename your track, change its colour or split the track here. You can also split a track by moving the playhead and clicking “D” or split at a particular section of the track."}, {"post_id": 55109, "ts": "2026-08-26T15:55:11Z", "hours": 5.6667, "text": "Ok! I added some core features that I was supposed to add before. You can now move your tracks in the timeline. Your supposed to click and drag the waveforms horizontally. I also added settable BPM. Now your BPM is visible in the top toolbar and I set the range from 40bpm to 240 (By default it’s 120). Also your bpm is saved and loaded with projects."}, {"post_id": 54901, "ts": "2026-08-26T03:51:31Z", "hours": 3.4381, "text": "I finally added another instrument in the piano roll. You can now select between an acoustic guitar and a piano. I’ll add more in the coming days."}, {"post_id": 54612, "ts": "2026-08-25T17:09:21Z", "hours": 6.9333, "text": "You can now rename and recolor all your tracks! I also fixed the importing from the piano roll to the timeline. When you click the recolour button it randomly picks a colour. I’ll add particular colour pickers in the near future."}, {"post_id": 53907, "ts": "2026-08-24T16:44:13Z", "hours": 1.2, "text": "Added these keyboard commands"}, {"post_id": 53836, "ts": "2026-08-24T15:09:08Z", "hours": 8.3119, "text": "-I FINALLY FIXED THE RECORD BUTTON. -I added a piano roll which can be switched to using the button on the top left (Probably might switch it’s location later). -It’s a 4 octave piano you can left-click cells to add notes and click them again to remove. -For now there’s only one instrument (Rhodes Piano). I’ll add more soon!"}, {"post_id": 53055, "ts": "2026-08-23T13:31:43Z", "hours": 2.4144, "text": "Tried to fix the record button cus it kept bugging out for some reason and I screwed everything up. I GIVE UP FOR TODAY. I’ll think abt this tmrw."}, {"post_id": 52840, "ts": "2026-08-23T06:13:19Z", "hours": 9.7575, "text": "Had a bit of a longer session this time. The last 3 days have been highly productive for me but now my holidays are over :( . I’ve made some big changes: -Added a record button. (Took me wayyy to long to make) -Added partitions so now all the sections are clearly divided. -Added a timeline. -Added a panel on the left (Still don’t know what I’m gonna do with it) -Added Save, Load, Export and Import features -Now you can trim your audio clips -Moved the add track button to the top left"}, {"post_id": 52371, "ts": "2026-08-22T14:14:53Z", "hours": 8.0044, "text": "Ok… I’ve got some huge changes. The DAW is starting to take shape now. You can now see the waveform of your audio track. I improved the audio quality drastically. I formatted so now everything is centered. I implemented a dark grey theme with neon blue accents. I’ve come up with the name… I think I’ll call it OpenDAW. I still don’t know what I’ll do with the space on the left but I know I’ll come up with something. My passion for this project is increasing every second I work on it."}, {"post_id": 51720, "ts": "2026-08-21T15:35:14Z", "hours": 8.4694, "text": "FINALLY…After 8 hrs smth mins of scripting and debugging I have this….You can add many tracks, change their individual volumes and play it as a whole. My plan is to make an opensource DAW which can easily be modded. I have holidays where I live so I’ll try putting atleast 8hrs a day. I need to format it because it feels pretty barebones rn AND I NEED THAT LAPTOP (i have a 4gb ram rn). Ty all!!"}]...
|
Title: OpenDAW by @saisrihari | Stardance
Devlogs Title: OpenDAW by @saisrihari | Stardance
Devlogs:
FINALLY…After 8 hrs smth mins of scripting and debugging I have this….You can add many tracks, change their individual volumes and play it as a whole. My plan is to make an opensource DAW which can easily be modded. I have holidays where I live so I’ll try putting atleast 8hrs a day. I need to format it because it feels pretty barebones rn AND I NEED THAT LAPTOP (i have a 4gb ram rn). Ty all!!
Ok… I’ve got some huge changes. The DAW is starting to take shape now. You can now see the waveform of your audio track. I improved the audio quality drastically. I formatted so now everything is centered. I implemented a dark grey theme with neon blue accents. I’ve come up with the name… I think I’ll call it OpenDAW. I still don’t know what I’ll do with the space on the left but I know I’ll come up with something. My passion for this project is increasing every second I work on it.
Had a bit of a longer session this time. The last 3 days have been highly productive for me but now my holidays are over :( . I’ve made some big changes: -Added a record button. (Took me wayyy to long to make) -Added partitions so now all the sections are clearly divided. -Added a timeline. -Added a panel on the left (Still don’t know what I’m gonna do with it) -Added Save, Load, Export and Import features -Now you can trim your audio clips -Moved the add track button to the top left
Tried to fix the record button cus it kept bugging out for some reason and I screwed everything up. I GIVE UP FOR TODAY. I’ll think abt this tmrw.
-I FINALLY FIXED THE RECORD BUTTON. -I added a piano roll which can be switched to using the button on the top left (Probably might switch it’s location later). -It’s a 4 octave piano you can left-click cells to add notes and click them again to remove. -For now there’s only one instrument (Rhodes Piano). I’ll add more soon!
Added these keyboard commands
You can now rename and recolor all your tracks! I also fixed the importing from the piano roll to the timeline. When you click the recolour button it randomly picks a colour. I’ll add particular colour pickers in the near future.
I finally added another instrument in the piano roll. You can now select between an acoustic guitar and a piano. I’ll add more in the coming days.
Ok! I added some core features that I was supposed to add before. You can now move your tracks in the timeline. Your supposed to click and drag the waveforms horizontally. I also added settable BPM. Now your BPM is visible in the top toolbar and I set the range from 40bpm to 240 (By default it’s 120). Also your bpm is saved and loaded with projects.
I finally found a purpose for the panel on the left. You can now see the current bpm, track count, track length, selected track, position and length. You can also rename your track, change its colour or split the track here. You can also split a track by moving the playhead and clicking “D” or split at a particular section of the track.
I finally added an effects rack. The effects I included were: Gain, Delay, Delay Time, Reverb, Distortion, Compressor, High-pass and low-pass filters. Each track now has an “fx” button or you can use the open effects rack on the left panel. The effects are added both to playback and export and you can use the “f” key to open the rack.
So I added undo and redo buttons (finally) in the top toolbar you can use ctrl+z and ctrl+y as shortcut keys. I’ve also added a looping feature. You can now enable the loop from the on/off button on the transport bar. Set A sets the loop start at current playhead. Set B sets the loop end at current playhead. “L” toggles looping and playback cycles continuously between Set A and Set B.
I added a drum beat maker ! (It shows up with an error tho whenever I try to play more than two drum tracks). It has a 16 step sequencer, A kick, snare, Hi-hats, clap. You can add cells by clicking them (they’re togglable). It uses your current BPM and is added to the timeline using the “Add to timeline” button. The code works by having separate parts for the tracks, audio engine, and GUI, which work together to load.
I added markers (they get placed at your playheads position). I added Auto-save which is togglable currently but I’ll probably change that soon. I added clip fades which can be set after clicking the fade in or fade out button. A dialogue box appears asking you at what time you need the track to fade in and out. I also expanded the undo/redo to 100 states which now includes fades and the markers. The project is close to completion!
I added a fullscreen arangement view. When you click f11 or the arrangement button in the top the daw expands into the timeline. It’s not perfect yet and it’s very buggy so I might remove it later (or fix it). The feature is inspired from ableton live.
I removed the fullscreen arrangement feature cause it kept bugging out and I wasn’t able to fix it. I’ll probably add it back on a future date. I also improved the vocals. You can now select your microphone from the input dropdown. You can record vocals when the instrument is playing. I’ve also added a vocal preset to the fx rack. The project is near completion (I’ll say around 3/4th way through).
I’ve added a settings window. You can now select your preferred microphone, MIDI and you can refresh devices if you’ve connected a new one. Also the keyboard shortcut list is displayed here. Settings is accessible from both the toolbar or the File dropdown
I’ve fixed some major bugs with the drum machine. I added a startup window which appears when the application is opened. It appears with entries where you can set your project name, BPM, Make a new project, Open an existing project or even settings. I made a new logo which is displayed at the top. I’ve also added a zoom in timeline feature for better viewing in the timeline.
I’VE ADDED TWO HUGE FEATURES!!!: 1)Full Mixer- So now we’ve got a dedicated mixer screen with volume faders, pan, mute/solo and a master channel. The colour of the tracks correspond with the mixer track colours. 2)Fixed Timeline Zoom- The zoom is now visible on the left panel. It changes the timeline width. The zoom is also saved with projects....
|
1791822313.0545862
|
1792686313.0545862
|
|
836
|
meow auth by @moon | Stardance
|
moon
|
0
|
129.27
|
0
|
[]
|
[{"post_id": 76283, "ts": "202 [{"post_id": 76283, "ts": "2026-10-03T02:33:15Z", "hours": 10.1333, "text": "pew pew cabosh, wawah space birb is very sleepy. BREAKING NEWS, i added and changed a lot of things so i could make a delicious soup of changes that changed my old changed code that i yes. minimal frontend work called making the passkey section button go crazy with colors, authenticator icon (just search “aaguid explorer”, its not crazy work) and having a dialog telling “hi do you want to rename this, no? okay”. I also have collapsibles that hide info from your lil nonfluffy face when its useless. Now my crappy server knows from where you are connecting (who would have known that a server could know that) and where you live (oh no [its approximated from your ip]). THe latter only works when the instance is proxied under cloudflare (it even gets mad if a request doesnt have the headers it wants when you have enabled the “under cloudflare” mode). the audit log single method to do everything™ has been swapped with a builder (i love this), AND its info now includes that same ip and location (actor related [who did it]). all fields are now obligatory for… audit log purposes, because before i just pushed null on the db for things i didnt want to show the user MAKING a pretty stinker audit log that does not show who did what. i really dont know whata past me thought, because you can keep everything on the db and just show what you want to the user. idk, past me thinking in uhm not portals (yes). okay, more slurry of things. mah beautiful fix so the JWKS (signing keys used to… sign a jwt! [important for OpenID]) update when the database JWKS changed, spammed the hell out of the db and log as it polled instead of waiting for the db (or an instance) to say “I UPDATED IT GUYSS!1”!1”. This comes from the instances cache’ing JWKS instead of saying “could you repeat that” to the db (too many requests = blue elephant cry and explosions), so they need to know when the db changes or THEY WILL BE out of sync!! mr past me also thought that it was a good idea to store the oauth token code (used to get an access token) in plaintext in the muffing db, SMTH YOU SHOULDNT AT ALL DO. now its just hashed via hmac just like everything else. man i am GOOOD at this job of so called programming, having to go back to edit a thing i called done AND then HAVINGTO edit and edit and edit. this is so fun that I WILL BURST into FLAMES because of how stoopid i am meanwhile, you also have me deserving to be cooked with rice because I made a condition in the oauth token exchange that when SOMEONE somehow miss types or gets access to the oauth token code but has the wrong client_id, the temporary code gets deleted… and you know what i forgot? i forgot to commit the changes to the db, so the code never was deleted lol. i love these kind of stupid goofy pain errors that arent even errors. AALSO I reprettyfied the oauth helpers because after working on them a long time ago, i didnt know what were they doing and it wasnt fun nor funny >:( [i was mad {i am still mad, with that space birb… i want to sentence past moon to sad corner jail for 12 seconds}]. NOW teeny tiny additions!“1 the ratelimiter now exposes a “Retry-After” per a hacker news old moldy article i found, plus exposes its headers on cors. the audit log now does its job even in more parts! now it also tracks the totp recovery code usage [i frankly forgot about it :(].Changing your email or name now requires sudo minus the name on register. AND the app startup has now a beautiful banner saying “meow auth” in cursive. its pretty :3, makes it stylishinning. I also moved and added to the service manager (the watcher) a way to have service names and to launch services from inside of it (for some reason i used a method that was outof place for ages lol). oh no my time has come nnonono, i give you permission to see my trashy work in screenshot format (so modern)… yet i shall push towards completion of this meowing meow meow authing meow auth server:"}, {"post_id": 65650, "ts": "2026-09-10T22:39:28Z", "hours": 11.2656, "text": "hi hello hiiii, stupid space birb back from long bed sleep break. CATCHING UP, i have swapped to resident passkeys, fixed jwk refresh feature not working, beautified toml settings, added 2fa replay protection and audit log beautification. alright.. prepare for a long boring devlog as always. I finally swapped to resident passkeys after thinking about it for days (hours)! That means.. NO MORE user enumeration problemo with passkeys, yippie. it wasnt really a pain in the feathered buttocks, like it was just enabling a crate flag and boomy boom (and it even works on the frontend!). also, i patched with scotch tape a stupid buggy bug about the email templates, becuase I used a lazylock to store the email templater engine stuffy, when SOME random goofy template or data was wrong for whatever reason, it would poison the lazylock and the next time it was used it would just say “oh noes, i am broken, poisoned my childs. GO HECK YOUR SELF”… i just fixed the template part by checking if tera likes them in the global state creation. i love scotch tape, it smells great. while doing that, i finally addressed the beautiful jwks not updating on all instances because in MY JOB QUEUE only one running server instance can eat the job. that means, if you have 2 instances running, the one that is the unfortunate one that didnt eat the job will have stale jwks until the next time it is restarted. fixed it with some beautiful blue elephant database screaming “HEY GUYS, THE JWKS HAVE BEEN UPDATED” and the other instances will be like “oh, okay, i will update my beautiful jwks now”. finally, 2fa, or as i like to call it, TOTP, has been taken cared like a baby and then thrown again to the battle field after giving it the power up of replay protection. That means, if you use a code, it will be marked as used and you cant use it again (this only works with valid codes). makes you wait more smh. THE SETTINGS got a layer of microscopic makeup because i was like “man, these setting name are weired? what if the settings file had comments to know what the heck is this beautiful knob named ‘do_not_touch_this’”. yes i did that. it was a big red booty pain as i had rerereremember how to do proc_macros and how the heck did i even update the toml files with new entries without making them implode with gibrish. AND WE ALL KNOW NOW that ‘do_not_touch_this’ is a setting that if you set it to true, it will make this space birb implode in flames and become dust (yes, like a real firework going CHIRPAAAAAAAAAAAH pop ). and the final thing given some body work spray was the audit log. no more caveman moonbeeper style ‘log’ method with a thousand things. now we have the SAME thing but as a builder with a ‘save’ method. it just makes the code easier to read and understand. also, the audit log now has a “what resource¿” and “what ip?” and i removed the audit entry making from inside the auth helpers to outside. it was just an inconvenient place to put it, i dont evne know why would moonbeeper put it there… oh right i am moonbeeper. I DID IT BECAUSE it was a great place in those times. alright, HERE’s your typical bad screenshots that are trashign and god i am sleepy:"}, {"post_id": 53541, "ts": "2026-08-24T04:13:41Z", "hours": 9.2344, "text": "hi hello this is space bird. i have eaten 9h hours of real world time in just making the dialog component. I mean, IT WAS for making the 2FA prompts for when you wanted to enable it or disable and bleh bluh.. atleast it does look great… and i still need to add icons. the prompt is just the typical 2fa enable that gives you a qr or the 2fa secret if you cant scan the qr. The dialog component was pretty much made in nothing, in air time (free :D). the thing that took a bit was just the styling BUT WAHT did take a BIG LOOOT of time was just making the 2FA dialogs. lik gha damn hecking “2FA prompt” and “beep boop enabling dialogs”.. why?! becuase they are PACKED like a lasagna with forms, tanstack fetching, dialog steps and other stupid stuff to make it atleast not confusing for the user to navigate with tree sized buttons that dont work (becuase you are on the wrong step!!!!1 or already did everything lol). i mean, besides that custom stuff, it just reuses components like the pin or qr svg and… for now, also uses the spinner icon on the buttons you click to manage 2fa as i couldnt be asked to add real icons for now (leaving them for lasts). next up working work is more dialogs (for the passkey setup [so it isnt just a spinner without any “magic guideance”]), a collapsible comp to wrap the info items to show EVENTUALLY some metadata like, in the session items, browser, ip, what user was that did that thing and etc. Its just so i dont balloon the “info items” BECaUSE they are supposed to be some kind of “quick info”. broom brroom, i love wasting time on these stupid things (and wiring them!) >:3. LEAVING that tiny goofy work that apparently took hours to do. I am thinking and WILL be doing a beautiful swap into using resident passkeys (the ones that dont require me to know the user’s email in advance). yes, i know i said before that i dont wanted and ew ew resident. but man, its just easier for the end user to click a magical button to login directly. AND AND this even protects me from someone slapping the meowbackend with the email fish to see who is registered (with a passkey) by checking if the user’s email makes the selector appear. still, some part of me still thinks this is really bad idea but i want to do it tho… what could go wrong hah! i might aswell go to the cooking pot already and make some uhh chicken soup made of space birb. that’s it, i still feel like i havent done anything. Here’s your beautiful video of showing the beautiful dialog going “hi” “goodbye” (the video shows me enabling 2fa, viewing recovery codes and disabling it [with a recovery code]):"}, {"post_id": 47860, "ts": "2026-08-16T02:59:32Z", "hours": 10.5872, "text": "would you look at that, 10h… i love wasting time. Now THIS thing fetches some bits of data like the audit log, my passkeys, adding those and even modifying the username (you are forced to do it)! BUT big buty, i had to add the mr /signup/complete where you set your prefered username, like moon (its a me!), or else the backend will straight up refuse anything you want to do. AFTER changing it, you can do everything else a normal user can [by Default the username is just the user’s email AND THAT IS NOT PRETTY AT ALL >:(] FIRST OF all i grabbed my magic cup for tea and made the audit log page (it does not even have anything special)! .. oh right, for that i needed to know how the heck you even send requests with tanstack. so uhh,, blah bleh bluh wawa BOOM i now know how to call a method and get my data back AAnd even make infinite fetchers (IT kinda hates my way of giving data [its wrapped in {3} onion layers {request.data.data.data}, the ones that make you cry a bit]). i also synced my auth state with the tanstack client so it doesnt do two trips every time. okay now. TO make the audit log thingy, i did some beautiful matching so the type, creation and guy who did the action (probably the user) are actually human readable with a limit of 8 items (or else the /me page would be as long as this project’s crappy devlogs). When there’s more, a beautiful “view more” button appears (separate page). For this, so called “view more” page, i used the infinite queries. while at first i just puuushed the data into the dom with the #each (svelte thingy), chrome do be VERY very mad when there’s a lot of entries in there (The audit log, because it logs actions in your account, can get very huge. Because actions like the baddie Session action logs each time you log in or log out), SOO i did what someone whose browser is eating more than 10gb does, using a virtual list to render the it! (IT only renders items when they are visible, hidding [removing from the dom] non visible ones. this fixes the “OH gosh THOSE 200 things THAT I CANT EVEN SEE, ARE EATING MY precious ram”). OHh and you can press ESC to leave the page because i made a cool component that lets me plug in a callback for specific keyboard events :D. SECOND, the passkey stuff. yeah, pretty easy thanks to libraries like SimpleWebAuthn that do the hard work of telling the browser “hey, authenticate this guy please” or “hiya, registrate this passkey please”. its just querying what passkys you have to know… hwat passkeys you have and that’s it. BUT sadly, an actually sadly, my passkeys are non resident (I cant have a simple button to just login, I need you email before i can use the passkey). Thaaat means I havea teeny big tiny small user enumaration problemo becuase the backend can reveal that an user has a passkey registered and, in turn, we know that user is registered. a bad guy can just atack with “oh noes, your passkey has been stolen. pls give a undecillion of moneys”. third, i had to add the sudo page. It does what you think, reauthenticate to do sensitive operations like adding a passkey. Pretty simple, it redirects directly to an email code if the user does not have a passkey or totp enabled (email code is always enabled), if they have those, it just shows a selection screen. fourth, i had to fix up some stupid things with the api schema of the backend becuase I either forgot to put a body on the responses (like the passkey/webauthn routes) or just fix up a teeeeeny tiny setting thing that wasnt correct (its the webauthn/passkey rp_origin [allowed pages to register/use a passkey} becuase it jsut used the backend url and THAT is not correct). so tahat’s it, shall you see some stupid screenshots and not even a video because i havent even finished everything:"}, {"post_id": 44382, "ts": "2026-08-10T02:59:15Z", "hours": 6.0403, "text": "well hello again. space birb presents you the next devlog. I cleaned up the forms a bit (they are a still bit TOO messy ugly but they do work), REDID the ugly flags class to be a more EEEASIER TO WROK WITH (i dont even know how it worked before lol) and FINALIZED the auth pages! RIGHT right, first frontend. I finally finalized the most basic and empty and everything pages of the frontend! wahoo mee :). these pages practically include the otp one and the 2fa one (+ recovery).. and i just realized that i forgot about the passkey one, great so now it isnt finished (fried space bird thingy). Redid the pin component so it works with my Input form component AND also fixed some weired quicks of the pin component in phones (for some reason, my phone, didnt respect the max lenght of the input. i just sliced the value of the input on my max length lol). And finally, the frontend now has the most precious bit of any… i dont know. i just added the typical dropdown componetn for the avatars (yes, this was important for me). while on the backend, because mr past moon made some weired decisions, the 2FA recovery code generator and giver of the code the state was a messy mess that I frankly dont know if I even tested it, because it accepted wrong codes? i dont even remember if it worked haha in the past BUT now it works as it should and its at least better readable with normal method names (not plainly “recovery” [… what does that mean anyway!?? you would need to look at the code to understand it! and that’s bad]). ah, the typical moment has arrived, the one i always repeat. HERE’s your happily working auth pages for once, plus the “want to retry the email sending?”, the passkey one is still missing but that’s just slapping some paint (and painting my self) and boom (whhile this is practically the same as the past devlog, it just works better than the past devlog lol):"}, {"post_id": 43668, "ts": "2026-08-09T01:58:51Z", "hours": 10.3242, "text": "would you look at that! Finnaly some functional pages with forms, ALmost finalized components… oh they are just the login pages….. and its not even all of the auth pages, just selected ones so the auth flow barely works. BUT, THIS fLuffy (not fluffy) space bird has finalllizated converting (or thinks) the classes i had as global styles into beautiful svelte components with all those stars and happy things. happily, i am longer limited by having evertyhign in global space.. oh right, RIGHT. i wasnt even limited :). AND the coolest compponent of all of them is the spinner one because it spins. The first pages have been plugged into an outlet called SuperForms (not really super, mostly helping to validate)! thanks to that, we now have working forms that post to the backend! with all those classic things, red colors and form validation. but, how I THE unfluffy space bird is pushing envelopes with the form inside of it? by talking to my backend obviously! but not manually (doign every step each time manually), by using a library like the trusty openapi-fetch that eats the openapi schema of the server i used in the past iteration. BUT i trashed it into the.. trash because I was fed up by having to repeat a thousand times the same snippet of code in each form or even page (I could have used a service like class to make it easier tho!). it was swapped by Orval! Orval works with the same openapi schema BUT gives methods! No more repeating the same things, just use the method and boom. and also that openapi-fetch was mad that i used a more recent typescript version. ANND, finally, the auth context was upgraded with a leg made of clay so it can now parse the BITENUM flags of, for example, the user (UserFlags) that tell what is enabled and what is not, if you didnt remember. Its practically a generic class with everything that i considered necessary that must be extended to be able to use it. and that’s it. you may watch the shittiest video ever that just shows that it kinda works with the forms and logging in and that’s it with still mocked things:"}, {"post_id": 36040, "ts": "2026-07-27T21:16:15Z", "hours": 6.8131, "text": "haha 10 days later. no progress just me rambling and going in circles. I am a bad space birb. RIGHT! I just were converting the css stuff into svelte components using the style tag (and the preprocessor duh). That’s actually it, quite literally. No progress at all. Wasted time on, still, falling on the loop of trying to get something that “looks good”, but i dont reall know what i want in reality. ofc i try to get “inspiratainionion” from other websites and try to mix them together to then dislike the result, undo and rinse and repeat (at this point my fur and feather soaked hard on water). YOu may imagine it as thinking that you ahve a finish line when you done even know where it is. i will just try to make the frontend work first without thinking about the design… AND i cant even promise THAT! because i will probbaly spiral into the same loop once again (saying oh this looks bad blah bleh bleh). god i am bad at making frontends or evne design or everything, but thats how people learn haha. it is a lesson that i should learn from now on.. i keep judging my work too harsly but i dont know. frontend is just harder for me. here’s the useless non functional three pages that atleast have something layout:"}, {"post_id": 30020, "ts": "2026-07-17T08:27:04Z", "hours": 1.9356, "text": "in the end i gave up in the design or well… can you even call it “design” when it was ugly even at teh start? yes, i tried my best at making it look “good” in my own eyes but i could not for the love of birds. it was bad anyways, like actually bad. I, to make it easy for my self and be able to copy how other people do it because i am a lazy bastard (stupid space birb that klanks its keyboard for nothing), will be using the typical two columns for each section. One for “what the heck is this section for” and the other for the actual content (wow). man, i hate how the frontend is where i struggle the most when the backend should be where i should have struggled the most, not with this dude. agh. I also hate HOW i thought the layout i wanted initially was the best one and that i look great when in reality it was an aberration that didnt even look good and it really threw to the trash the unused space bruh. it just wasnt for this job, maybe for something else.. idk haha, what I know is that I think it already looks better with the simple sutff ive done lol [that’s sad btw :)]. as you may see int eh screen shooting, the audit log section instead of having a table it uses a… placeholder for a button with the same content it has currently because I frankly i am scared of making tables because of the probable difficulty i will have (because I am already struggling with teh simplest of things!). ALSO, i need to stop making .scss files, compounded into a single css file using @layer and @use , to just use svelte components that use the style tag. it really doesnt change anything, but atleast i wont be doing “class=‘meow’” each time i want something, just using “” is half better lol (doesnt really fix anything, I just want to give use the svelte components). finally, i am planning to use “services”.. they arent really services but more about wrappers around the openapi client. less manual labor for some shitty stuff that i wont need to do anymore. Still, its just a wrapper with nicer method names and namespaces. (also, ill be adding profile pictures. didnt think i would be adding them tho) here’s the typical dumb screenshot of what i think that looks “good” without doing that much work:"}, {"post_id": 28705, "ts": "2026-07-15T03:47:05Z", "hours": 8.9958, "text": "i am ashamed. i am that bad or… well ive gotten to the point of real badness and what some call “skill issue” because I CANT make this hecking frontend. its just too hard, i am just way more slower than before and what i though i was. i hate that i am not usign tailwindcss and isntead I am building my own scss styles IN @layer when i could have been using svelte components with the style tag but ugh bad decisions that can be still reverted i guess. For the layout of the settings, i even tried copying how I did it in the last iteration of this frontend (past server, not named meow auth).. i just did not like it because using a sidebar for this teeny tiny amount of information is wasteful (like this tiny [not really tiny] space birb). Like the content is just the typical name, email, auditlog, 2fa, passkeys, sessions, oauth authorizations and user created oauth apps AND lets not forget about the admin panel. i am practically suffering with mundane crap that anyone can have done in probably 10 minutes by asking mr claude or even chatgpt OR even just copying an already made layout, but here i am suffering trying to make stuff my own way, telling my self that this was the worst decision i’ve made goddamit… at this point i will really become a hotpot space birb soup.. i just need one layout made and then it should be an easy job to then copy and paste to other tabs. then the enxt step is to wire it up the forms and have it communicate to the backend which should be an easy task (i think [… oh god please be easy])… i know this entries of the frontend will make my project go into deflation mode and stuff, and i even broken a promise that ive made my self to not make devlogs that are more than 5h long because of a review someone made of my project when i submitted it unfinished to flavourtown (yes, flavortown) which is sad but it is what is. you cant be perfect, that’s why i have a shit hecking design. i am just bad, a bad space bird i guess. i already rambled too much in this suppossedly simple devlog. here’s your half crap “inspiration” that I used to (quite literally did a C&P of the page) of my past proyect using a sidebar (not what i want with this iteration) and the current state of art of my REALLY REALLY SHITTY curernt state page:"}, {"post_id": 25277, "ts": "2026-07-09T13:32:20Z", "hours": 10.9092, "text": "hi hello. oh, ive been 10 hours doing the same thing!!1 god. So, I was doing the frontend design without using figma and uhh yes. And also removed the ability to use your login to.. login to the app. You just use your email (otp, and passkeys). First, I forgot how to use figma, I only remember some shortcuts. I was going to use it (for frontend and logos) but seeing how struggled to make simple things, I only used it to swap the colors of the logo of MEOW AUTH, hmm yes, very meow. . The frontend stuff… I frankly ALSO do not remember anything.. or well I do now remember some things thanks to me fighting to uncenter a div hah. The stack is practically svelte, as always, client rendered and scss (sassy :o) for the styling. In the start I was just going to use the same styling and structure of my first iteration of this project, beepauth (ew), and after doing all of that I thought “god, this looks pretty bad”, soo I then tried to make it look a bit better WITHOUT starting from zero again. And I did style it a bit better but I didn’t even like it. Like it felt like I hadnt even advanced at all, like if the same shitty fricking backend was the same from the past (probably noone would care because probably uhh noone cares.) [brains a weired and even more mine i guess, wawa]. So, naturally, i did the less sane thing, REDOING EVERYTHING again. Had to watch tutorials to relearn how use sass and picked up also some new css stuff that I wont even use (minus the layers! that pretty cool!!). And from that css stuff I discovered Oklch.. yes i swapped my whole color palette to that using their harmonizer that uses the equivalent of WCAG, APCA (contrast bg to text!). At least the frontend now looks decent (or the first blocks fo the website). sadly the only frcking shitting heckign parts ive made are the login (and register BUT THATS THE SAME). AWIT WAIT WAWA, I also swapped some gears in the BACKEND! I removed the login (username) entirely, now its just demoted to a non unique display name (for openid!). you must be asking why, because I changed it to use the login not long ago. Well, because I don’t have to force a user to give me a username AND their email on signup. Its easier to just give the email. And if you already have an account and hit, without knowing (our typical autopilot mode), the signup page, I can silently make a AuthChallenge for login instead of signup because both handlers return the same result. Then, after registering, I just force you to set a display name (by default its just the email, i could leave it like that but currently I want it to force you set a custom display name for OPENID). By doing that, I practically removed the “vectors” (ooh hackiry stuffiry) for user enumeration [those vectors were the endpoints to see if a login (username) or email was already taken] (I also made so the login endpoint gives you a fake challenge id when the email doesnt exist {This works thanks to having the auth exchange handlers return “invalid code” even if the challenge doesnt even exist}). here’s your screenshot of a login page that looks exactly like very other login page, maybe even shittier (sorry i am bad doing these kinds of things.) [also you have the logo colors, i went with purple because it looks cool and its a cool color] {the “Psst, over here” its just a random greeting, just like the ones i have in the emails} [[sorry for wasting 10 hours doing nothing]]:"}, {"post_id": 23063, "ts": "2026-07-05T20:02:59Z", "hours": 0.3025, "text": "a. hi there mr svelte! I was wondering if I should be drafting first a design in figma but meh. I like to suffer and make bad designs that progress very slowly to look mediocre at best! yay!1!! anyways, ill be using scss as always (i frankly done even remember anything about it lol) and sveltekit. HERE’S YOUR stupid “hello world” from slevetkt (reaaly useful devlog):"}, {"post_id": 22522, "ts": "2026-07-05T01:24:44Z", "hours": 0.3122, "text": "oopsy. shoooort one, sorry. I added the forgotten oauth error StatusCodes and Descriptions. there’s not much to it lol, I just forgot about it. as always, here’s your even uselesser screenshot with code taht you can already see in the guthub:"}, {"post_id": 22514, "ts": "2026-07-05T00:59:48Z", "hours": 1.7131, "text": "right, its a me! the teeny tiny waist sized space birb that is here to tell you that I added the ratelimiter! It works just as i’ve explained in the last entry, it uses the IP of the user to limit the amount of requests via a ticket bucket. THe bucket is stored in memory, as again said before, so if the instance is restarted, goodbye ratelimits. EAch request eats 1 ticket AND the bucket tickets and refill rate can be set via the… middleware. who would have known wow. It always adds the typical “x-ratelimit-limit” bleh bleh bleh with all the info you can like that is useless BUT hey, its there for you. I like making my own stuffies because its cool. THe root has a limit of 500 requests, refilling after 1 second. While the auth routes have a limit of 10 (sudo) and 20 (auth flows) requests, refilling after 1 minute. The other routes are just uhh protected under the umbrella of the root ratelimiter lol, no need for having to add a ratelimiter to every single route (god). that’s it. there’s no more devlog. Now what i gotta start doing is the frontend. I will cry a bit because I am VERY very VEEERY rustified in the typescript department like I never touch it but meh I MUST do a frontend or else this is incomplete… and i will do be crying if i send it off in a incomplete state. THE frontend will be in svelte kit, because its cool and ive already used it in the last iteration and it left me with a good taste in my forked tongue (hahaha, i am space birb) AND thanks that Ive been using from the start utoipa to add an openapi schema, I can use it easily to make requests from the frontend! and i didnt need to suffer adding them after everything was done just like in the past iteration! here’s your useless screenshot showing teh ratelimiter working and the headers and stuffies… god i am a mediocre at best writting long hecking devlogs for nothing:"}, {"post_id": 22325, "ts": "2026-07-04T18:52:19Z", "hours": 1.4481, "text": "wow. that’s a long time doing nothing. I just updated the deps, tera had a change on its templating because it was rewritten from the ground up. pretty cool, now it kinda looks like jsx and at the same time not. I also tried or well… I am thinking about making my own ratelimiter but was like, ugh. I was practically mega blocked on the idea of wanting a ratelimiter that used the IP + UserId. Both are easy but for some goddamn reason I kept going circles and circles and honestly I dont even remember why anymore, like huh!? In the end I will only be doing ratelimits via IP because they are the easiest AND THEY DO NOT NEED TO be stored in the database. yes database, you read it right. IN this version of my backend I wanted to use the least things AAANd the oNly thing required is just the database to make everything. Adding redis would be waste (and waste of ram) for just storing the ratelimiter. Redis would be required if I wanted to run more than 1 instance of my application AND IF i wanted to distribute the info about “oh this guy is grounded, no entering the meow auth realm for 5 minutes” to all other running instances. BUTTT, as you might not see, I dont want to run more than instance nor want to distribute it to more instances if it were the case that I wnted to run more than 1 instance. Soo, I went in circles in my crapppy bird brain because to add ratelimits via the user id I would have to store the info somewhere that wouldnt be erased (like in the in-memmory storing that i will be using) or else the ratelimits would be useless because… THEY WOULD BE ERASED from this whole realm when the instance is restarted (memory goes boom to the void, so does the ratelimiter). That’s why I want to just implement the Ip based one, its the easiest one and the only one that I don’t care about loosing the information about the ratelimits when the instance is restarted. Also, i need to implement it yet lol i was just scrambling my head nuts and bolts hearing them clank while trying to ratchet them. ALSO², i need to redo the email stuff because my brain has came up with a cool design lol but first gotta do the frontend to make it uhh look good before suffering making the email components. I also started to hate the tera zed language extension. its crap. bad crap the worst one. ALSO, here’s your really useless screenshot this time showing code that you can already see on the github page (yay!1):"}, {"post_id": 20456, "ts": "2026-07-02T01:37:49Z", "hours": 5.1653, "text": "hello again, its a me, a random space birb that is here to tell you that I completly forgot about the ratelimiting stuff. BUUT, I added the admin methods and UserFlags to be able to set… flags. not much rodeo this time. had to think how i wanted the audit logs to work for the admin stuff. I first though about just having a bool saying “ah yeah admin” but meh, I think its better to have also an “actor_id”, the who did it user id, and boom. In the user side handlers for the audit log I just return the typical plain response of “yeah this was changed” (not sql joins, cheaper [?]) but in the admin side, I return the actor_id with some other things like the user login of the actor AND the user login of the user that was changed (cause why not!? its jsut another join statement in sql). Works wonderfully and even better now because before I had setup the user audit log to fetch AAAL the entries form the db and pee pee poo poo no, now that i have my paginated stuff, no way! SOO, the admin methods are just the typical ones, see what the user has changed or an admin changed from an user via its audit logs, the oauth applications and authorizations management and that’s it. I will probably also add a way to know globally what the heck is being changed but idk about that, seems a big sql query (that is tiny but very costly in db terms) and I frankly want to avoid that… and even more avoid using redis because i hate it and that SOMEHOW i have better some beautiful organized code vs the first version of this backend where I used the typical DB+redis aaand it was ugly . i hate that. I also added the UserFlags to be able to set flags like “is the user an admin?” or “is this user allowed to manage its oauth applications?” and so on. Pretty cool. BUUT as always, I had to C&P some code (almost all of it) from past handlers to make the admin handlers work lol. I would really want to make a central place to put all the common handler code but meh. IT WORKS! and that’s what matters. THe next things are just making the ratelimiting WHICCHC probably will be only inside the instance the user is reaching and that’s it. I would like to have global ratelimiting using the IP of the user or its account id but that would (i think) require redis… I really need to think about this because someone with a crappy toaster can make explode my poor baby vps. ps. god do I really take that much time doing stuff? god i am slow at coding. i am bad goddamit. that’s it. shallllll you see my shiitty api docs with the admin stuff and admin auditlogs becuase why not:"}, {"post_id": 16949, "ts": "2026-06-27T14:57:54Z", "hours": 2.2208, "text": "hi and welcome to my unreadable journals. I added those oauth authorized applications management handlers that I forgot to add yesterday, like the typical “oh let me see what scopes does this thing use… OH GOD IT CAN SEE MY fur color I MUST revoke it NOW!11”. Then I moved AAAL the handlers that touched ME user data to the.. obvious /me route so its prettier and doesnt pollute the air of the real auth related things. and did some cleanup in the http error department by merging some repeated errors. god this is one of the shorter entries i’ve made i think. next up is doing ratelimting, probably roles or just an admin api and the “hey your account has been locked because of repeated tries to login”. i dont even know what will it take to make tests work god. here’s your shitty screenshot showing my only progress, the oauth management bleh bleh bleh:"}, {"post_id": 16427, "ts": "2026-06-26T18:44:11Z", "hours": 3.4175, "text": "hi, hello i am space birb in the sad corner and i am here to tell you that i added the the typical oauth management handlers that everything has. I also added a generic paginated database thingy to be able to make… pages when there’s more than 20 items (rows). firstly, everything was easy. yes, no rocks not nothing that could set me on fire… minus that I HAD TO MAKE a paginated database thingy. WHY did i make it? idk man, i wanted because my bad coder brain thought that maybe someone could have more than 1 oauth application created (like a thousand) and that’s pretty bad for mr blue elephant database becuase that means eating 1000 rows in one go (and blue elephant doesnt like that, AND EVEN less if my vps has 1vcore and not really that much ram). SO, the best course of action was to make pagination to be able to cut in pieces the data and make it easier to load and handle.. even though the loaded rows are hardcoded (its 20 plus 1). The pagination practically works by seeing if there’s more data after 20 items (that’s why it has a “plus 1”!), if so, we take the LAST item’s id (using a trait to access it, because you can’t really know in what form or what id will be used in any database model) in those same 20 items to set the ‘next’ param. That ‘next’ param is used to.. quite literally say “hi, uh could you give me the next rows after this id pretty please?” and boom works and i didnt set my self on fire but i did ruffle my feathers a bit because of. Ill probably implement it to more places because the only places that use it are the oauth applications and user sessions because i am a bit lazy haha (i mean, its just C&P the current find_many* and use the pagination stuff and caboosh). oh god, i forgot about the oauth consent management. great job me, NOW IT ISNT COMPLETE “everything” great coder i am great big bird brain. My next plans are just to do some general cleanup with the openapi schema, add comments to the code and then start doing the frontend in mr sveltekit.. although i am too rusty in ts land and i will have to learn some stuff again and touch again scss and stuffies and god, i am going to get cooked in the sun and roasted like the space birb i am because of me no longer touching anything javascript. here’s your really useless screenshots showing the INCOMPLETE oauth management handlers and the pagination thingy working:"}, {"post_id": 15504, "ts": "2026-06-25T06:20:57Z", "hours": 1.2603, "text": "I made the User Info handler for the open id stuff. that’s the end of it. It works, wahoo my self yay good job, even though you had pretty much bad scrambled code, you did it hahaha. It practically, as the openid spec says, returns the claims used also on the id_token. Soo, because its sooo close to the id_token, i’ve decided to just eat the that same id_token but spit out a converted version with things I want. I also added the typical OauthManager and RequiresOauth. the latter one not having a param to decide what scopes you want because, whhy would you want that?! its just a one liner and it may change for each hecking endpoint or even the data the endpoint returns. its just over complicating it with wrapped trash. … and about my devlogs, i realized that mine are pretty bad and not interesting like other people’s devlogs. i simply write weird and long ones. but i don’t want to write a simple “I did this, commits, hardness of implementing, whatever” in bullet points like other people do. i dunno it feels flat like this devlog. Here’s you typical SINGLE screenshot showing that the user info works, that’s it:"}, {"post_id": 15052, "ts": "2026-06-24T13:25:06Z", "hours": 6.1178, "text": "oh hi, did not know that you are really reading this. I added the openid stuff! Well not all of it, I still need to do the ‘userinfo’ endpoint but meh, it works as it should. I also added the cool JWK (the secret key [ES256] that is used to sign the JWTs) rotation stuff, which I can say that I tried at first over complicating it to then end up simplifying it because it was A HELL to use like you had to call a thousand hecking things or in one method give a thousand arguments (its an overestimation, probs not real [shy of 5, it wasnt generic, it was attached to the token flow] :3), it was a pain to use and IT EVEN GAVE ME a burn mark. As I said, I just redo’d everything and boom, simplified thigny you just call from the global state and boom, we do be working! The JWKs are rotated every 2 months, and the old one is retired at the same time. After 1 month, the retired one is deleted. So, at any given time, there’s a maximum of 2 keys, the current one and the retired one. The retired one, as the name implies, is not used to sign new tokens but is still valid to verify old token. And all of this is fetched at startup and then via a scheduled job it is updated. AND as you might have (NOT) guessed the JWKs are stored in the database, so if you have multiple instances of mah meow meow server running, they all share the same JWKs and rotate… OH GOD THEY DO NOT ROTATE THEM AT THE SAME TIME GODDAMIT AAAGH.. the intention was there okay? I did that to, in one part, give more use to my job manager and, to let all isntances update at the same time but big brain me did not think that a job is always locked to the instance that picks it up. also the token endpoint now supports using openid and spitting out a ‘id_token’ with the user info (some claims are gatekeept behind the scopes [like ‘profile’ or ‘email’ duh]). that’s it actually, like i started really pulling my hair, fluff, feathers whatever because I was trying to overcomplicate it a lot and i really dunno why. like, for another example, i kept swapping from the ‘jsonwebtokens’ and ‘compact_jwt’ crates because I wanted to use ES256 but didnt want the other because i never used it but then ended up using the latter because it was easier, and then that I wanted to use EC25519 which is apparently not really talked about in the rfcs (that’s why i ended up with ES256! [also supported on compact_jwt]). AH! and also in the job queue, for the JWK rotation, I wanted first to add cron job for that but… why tho, I don’t really need it, I just need a way to NOT run a job until a specific time and then reschedule it inside the same worker. here’s the shitty screenshots showing the oh wow ‘openid configuration’, oh wowie ‘it does output tokens and id_token’, oh thats nice ‘it does give out a valid jwt with valid claims that the programmer didn’t even do themselves because they used a library called compact_jwt’ and wow ‘it does have a job that tries to autorotate if the current key is older than two months’:"}, {"post_id": 13022, "ts": "2026-06-21T09:21:29Z", "hours": 1.6031, "text": "you know? i love debugging my code when the error was the entire time in the db. great job me!!!! I’ve made the token handler. again, as other devlogs, that’s all folks. Stupid me couldnt figure out why client secret was always bad, why the mr code verifier was always bad when it was the same as the stored one aaand why the heck scopes werent showing. i really do love that my code, practically, worked first try (MY CODE >:3). But did NOT love that i am still stupid and forgot that i had a database that works as the house for the data that my app ingests. god i am smart. time to go suffer with openid i guess. i have to touch jwt and stuff, eugh.. i like my opaque tokens more than jwts, because IF you want to revoke one of them you still HAVE TO STORE A LIST OF IDS… that defeats the whole hecking purpose dude, not worth it (or that’s what i think for now, smh i am just a space birb claking at keys). here’s your video that shows that it does work and gives the bearer opaque token and creates the db entry:"}, {"post_id": 12940, "ts": "2026-06-21T05:37:44Z", "hours": 4.6994, "text": "yayy! the first step of making oauth2 alive has been made. I rewrote the tiny part of the authorization handler of the oauth2 flow… there’s not much to it! It did took a while to reunderstand it because of how long I wrote it (2 months ago) and that I wrote it in a rush (and didn’t even make it work). Basically, the same as always, traceback to try to understand what past me wanted to do, to then rework everything (or at least try) to unspaghettify those radiative peaces of code. So, practically it only the authorization method was half done and ugly, it was a big BIG monolithic method that did everything in a way that either past me or current me like at all because it takes a whole lotta things to work (8 to 9 argumentos lol). In the end i just splitted the big MONOLITHIC (thick) method into two of the same kind but with at least clear and defined and bleh jobs. One when the user has already authorized the client and the other when he has never touched it in its life. I also did some qol for my self (or i think i did, probs not because it was already bad and rushed and stuff… i already said it at the start) with the oauth helpers and stuff by also prettifying them and simplying parts that I really don’t know why I added. Like I added mr “OauthResponse” which, as the name implies, makes the oauth response (redirect, plain error, error with redirect) instead of the 3 different methods I had to use to do the same thing, easier is better (and even understandable!). Ah, and i even tried to use strum but nope, it do not cut the job (it tries to cut the wall instead of the thing i want). NOW, sHALl the work continue to make the token making handler and then the OpenID stuff that may be a bit difficult because, with my shitty reading, i think ill have to juggle making jwks and stuffies to server correctly the food for the picky openid. here’s the video that shows, confusingly, that indeed the handler works (It does a “hi, oh you want to authenticate with ‘poop’, let me create a pending authentication. Oh! you accepted the consent, let me delete the pending one and create the actual lasting consent for ‘poop’.). god, i am so bad at coding or whatever the this is:"}, {"post_id": 11753, "ts": "2026-06-19T04:14:50Z", "hours": 2.2278, "text": "AAAH, sorry, did i startle you? I added audit logs, a health handler (a simple “OK YES i am alive ) and a root info thingy that tells you “okay, so my name is this, mmh yes, and this is my version and my build stuffies…). the audit log thing was not really a pain to add. At first i was thinking of using my job queue (because ITS only use is for emails… and i put my sweat and tears to making it pretty to then not being used lol) but.. why tho, like IF you see how was the code before the audit log adavancement (yes, cement) you can se HOW every action runs already a postgresql transaction. So, why not bundle the audit log adding magic into that same transaction? and so i did. Now, we don’t have to track our selves with pencil (i dont like pens) and paper the actions we do, we just let the application do it for us… leaving a paper trail of our desastrous actions. yay… now that i said that, i should add a random chance of also adding another audit log entry that is just a random “hi, i am your audit log talking”. hah that would be funny. btw, strum is not cool. I thought i could use hte EnumString thingy to make it implement automagaicallytationally the Display trait to snake_case but NOPE, it is used backwards. You give it a string and it gives out the enum variant. what a pee pee poo poo dude, imagine having a big monstrous enum and having to implement Display for each variant… just like the monolithic error enum of the http site… ew. to use it you just sprinkle the audit::log() and boom, done. AAAAAAH, did i scare you again, no? yay. I almost forgot (<- totally) to say that… i think THIS IS really the last thingyes before the oauth stuff. i really think i am going to burst into flames and become a cooked space birb… kfa (i hope you get it [its a acronym of a whole word {also, hi and thank you for reading this long journals that may be very boring or unnecessary to read}])"}, {"post_id": 11100, "ts": "2026-06-18T04:45:50Z", "hours": 4.8661, "text": "ah, beautiful api docs. I reworked the api docs (because they were a messy mess because of my past laziness [not understandable!!]) and now they are organized in pretty folders (tags) with correct descriptions and titles. now its even helpful to read the api docs to understand how to use a api handler, not having to dig into the code [noone does that :(] right, I DID THE EMAIL CHANGE THING and LOGIN tooo wohooo…. yeah it isn’t that amazing lol. at the start i was just thinking about using the typical chacha encryption that ive used for the TOTP stuff but for a random token nanoid’d… you just don’t, hMAc is fine. Ended up with a dual like verification flow where both the old email AND the new email get a token link (well, now it just the token string because, ahem, i dont have a frontend). both have to be verified before the change goes through. sooo, no more “oh noes, not again, someone got into my account and changed the mail to their own, smh”. then you just get the notif saying “hey, your email has been changed”. SEEECOND, login (your username) changing magic that is way simpler because I just check if you have sudo enabled and that you havent changed it in the last 24 days (why not). Just like with the email stuff, you get a notification saying “yay, you changed it!”. and I think that’s the last of the tiny stuff i wanted to do before the BIG and PAINFUL oauth stuff. i think i am going to burst into flames and go boom and the floor is covered with dust and feathers. OH wait, i sitll need to do account deletion… whatever its just a simple “hya you need sudo”, “are you sure about this”, “ok, bye” flow. meh. i also need profile pictures… heh backblaze or maybe cloudflare idk i like backblaze more but uhh I GOTTA RESEARCH OKAY!?! shall you see the probabaly useless screenshots that atleast show the tiny progress that i always do. great"}, {"post_id": 3935, "ts": "2026-06-07T02:04:58Z", "hours": 3.2119, "text": "beep bwap. firstly, what a waste of argon2. I was using it for encrypting the otp codes and.. dude, that’s like its just 6 characters. I just swapped to the typical hmac256. I also made a common cookies helper method to not have 2 (or more) instances of the same cookie making method. uhh, also I adde dhte forgotten passkey management handlers… and that’s it. OH WAIT, i also added validation! I mean, its pretty rough and very bad but it works for what I want! I, in theory, was going to use Garde for the validation with axum-valid to provide me with the base stuff. I DID NOT WORK, like I had (and still have) to make a wrapper around their sutff so I can send out my own error messages and not some plaintext like before i handled them. right, it did not work because of something called the “Context” … I tried everything to make it work, but I just could not, not even doing workarounds and C&P examples directly and stuffies, it just would not work like it wanted the global context to have a trait implemented, which I did, but it still would not work. I guess its because to use my global context inside axum, i wrap it around an Arc and maybe it didnt like that? or whatever meh wa. I just swapped to using Validate, and ta-da works as it should. HERE’s your badly made screenshot showing the crappy validation messages (they just say: “this field is bad :(”)"}, {"post_id": 3560, "ts": "2026-06-06T15:40:12Z", "hours": 0.8356, "text": "added a better way to handle required authentication!… middleware. yup, i am very creative. it’s practically a qol improvement in the development side, making code look a bit nicer without seeing in every single protected route the same 3 lines to check if the user is authenticated. now i can “enforce” it when I want the user to be reallly unauthenticated to use a route. to use it, you just “add the middleware and maybe move one flag” and that’s it… goddamit I should have had that before to not write a heck ton of “if not authenticated or if authenticated…”. i am trying to delay the inevitable.. the need of making the oauth side and the frontend ;D"}, {"post_id": 3518, "ts": "2026-06-06T14:25:42Z", "hours": 3.6319, "text": "I started using a master key for everything and fixed the server returning a plain text message when parsing goes wrong. FIRST, I have seen the future and the future says that I WILL be suffering if I have more than 2 secret keys to manage when I can just derivate them from a single master key. AND EVEN rails does that! its just blake3’s key derivation with a xor to fill the remaining bytes that some libraries require. it works pretty great. neat. SECOND, this was a problemo that one of the flavourtown (yes flavour not flavor) reviewers made me notice. When you send a malformed json body, like a new line on a field that really doesn’t expect a new line, the server would plainly return a error message in plain text, which is really bad for the client to handle and even for looks. Sooo, I tried fixing it trying to strap a error handler to axum… yes I dont even know if that even exists bruh. In the end i just created a newtype that wraps the original Json extractor from axum to catch its errors and THEN and only then wrap those with my own error messages. And bob’s your dad, the server now return a json error message like every other route error. shall you see the useless screen recording (its cropped by stardance’s ui, great)"}]...
|
Title: meow auth by @moon | Stardance
Devlogs:
I Title: meow auth by @moon | Stardance
Devlogs:
I started using a master key for everything and fixed the server returning a plain text message when parsing goes wrong. FIRST, I have seen the future and the future says that I WILL be suffering if I have more than 2 secret keys to manage when I can just derivate them from a single master key. AND EVEN rails does that! its just blake3’s key derivation with a xor to fill the remaining bytes that some libraries require. it works pretty great. neat. SECOND, this was a problemo that one of the flavourtown (yes flavour not flavor) reviewers made me notice. When you send a malformed json body, like a new line on a field that really doesn’t expect a new line, the server would plainly return a error message in plain text, which is really bad for the client to handle and even for looks. Sooo, I tried fixing it trying to strap a error handler to axum… yes I dont even know if that even exists bruh. In the end i just created a newtype that wraps the original Json extractor from axum to catch its errors and THEN and only then wrap those with my own error messages. And bob’s your dad, the server now return a json error message like every other route error. shall you see the useless screen recording (its cropped by stardance’s ui, great)
added a better way to handle required authentication!… middleware. yup, i am very creative. it’s practically a qol improvement in the development side, making code look a bit nicer without seeing in every single protected route the same 3 lines to check if the user is authenticated. now i can “enforce” it when I want the user to be reallly unauthenticated to use a route. to use it, you just “add the middleware and maybe move one flag” and that’s it… goddamit I should have had that before to not write a heck ton of “if not authenticated or if authenticated…”. i am trying to delay the inevitable.. the need of making the oauth side and the frontend ;D
beep bwap. firstly, what a waste of argon2. I was using it for encrypting the otp codes and.. dude, that’s like its just 6 characters. I just swapped to the typical hmac256. I also made a common cookies helper method to not have 2 (or more) instances of the same cookie making method. uhh, also I adde dhte forgotten passkey management handlers… and that’s it. OH WAIT, i also added validation! I mean, its pretty rough and very bad but it works for what I want! I, in theory, was going to use Garde for the validation with axum-valid to provide me with the base stuff. I DID NOT WORK, like I had (and still have) to make a wrapper around their sutff so I can send out my own error messages and not some plaintext like before i handled them. right, it did not work because of something called the “Context” … I tried everything to make it work, but I just could not, not even doing workarounds and C&P examples directly and stuffies, it just would not work like it wanted the global context to have a trait implemented, which I did, but it still would not work. I guess its because to use my global context inside axum, i wrap it around an Arc and maybe it didnt like that? or whatever meh wa. I just swapped to using Validate, and ta-da works as it should. HERE’s your badly made screenshot showing the crappy validation messages (they just say: “this field is bad :(”)
ah, beautiful api docs. I reworked the api docs (because they were a messy mess because of my past laziness [not understandable!!]) and now they are organized in pretty folders (tags) with correct descriptions and titles. now its even helpful to read the api docs to understand how to use a api handler, not having to dig into the code [noone does that :(] right, I DID THE EMAIL CHANGE THING and LOGIN tooo wohooo…. yeah it isn’t that amazing lol. at the start i was just thinking about using the typical chacha encryption that ive used for the TOTP stuff but for a random token nanoid’d… you just don’t, hMAc is fine. Ended up with a dual like verification flow where both the old email AND the new email get a token link (well, now it just the token string because, ahem, i dont have a frontend). both have to be verified before the change goes through. sooo, no more “oh noes, not again, someone got into my account and changed the mail to their own, smh”. then you just get the notif saying “hey, your email has been changed”. SEEECOND, login (your username) changing magic that is way simpler because I just check if you have sudo enabled and that you havent changed it in the last 24 days (why not). Just like with the email stuff, you get a notification saying “yay, you changed it!”. and I think that’s the last of the tiny stuff i wanted to do before the BIG and PAINFUL oauth stuff. i think i am going to burst into flames and go boom and the floor is covered with dust and feathers. OH wait, i sitll need to do account deletion… whatever its just a simple “hya you need sudo”, “are you sure about this”, “ok, bye” flow. meh. i also need profile pictures… heh backblaze or maybe cloudflare idk i like backblaze more but uhh I GOTTA RESEARCH OKAY!?! shall you see the probabaly useless screenshots that atleast show the tiny progress that i always do. great
AAAH, sorry, did i startle you? I added audit logs, a health handler (a simple “OK YES i am alive ) and a root info thingy that tells you “okay, so my name is this, mmh yes, and this is my version and my build stuffies…). the audit log thing was not really a pain to add. At first i was thinking of using my job queue (because ITS only use is for emails… and i put my sweat and tears to making it pretty to then not being used lol) but.. why tho, like IF you see how was the code before the audit log adavancement (yes, cement) you can se HOW every action runs already a postgresql transaction. So, why not bundle the audit log adding magic into that same transaction? and so i did. Now, we don’t have to track our selves with pencil (i dont like pens) and paper the actions we do, we just let the application do it for us… leaving a paper trail of our desastrous actions. yay… now that i said that, i should add a random chance of also adding another audit log entry that is just a random “hi, i am your audit log talking”. hah that would be funny. btw, strum is not cool. I thought i could use hte EnumString thingy to make it implement automagaicallytationally the Display trait to snake_case but NOPE, it is used backwards. You give it a string and it gives out the enum variant. what a pee pee poo poo dude, imagine having a big monstrous enum and having to implement Display for each variant… just like the monolithic error enum of the http site… ew. to use it you just sprinkle the audit::log() and boom, done. AAAAAAH, did i scare you again, no? yay. I almost forgot (<- totally) to say that… i think THIS IS really the last thingyes before the oauth stuff. i really think i am going to burst into flames and become a cooked space birb… kfa (i hope you get it [its a acronym of a whole word {also, hi and thank you for reading this long journals that may be very boring or unnecessary to read}])
yayy! the first step of making oauth2 alive has been made. I rewrote the tiny part of the authorization handler of the oauth2 flow… there’s not much to it! It did took a while to reunderstand it because of how long I wrote it (2 months ago) and that I wrote it in a rush (and didn’t even make it work). Basically, the same as always, traceback to try to understand what past me wanted to do, to then rework everything (or at least try) to unspaghettify those radiative peaces of code. So, practically it only the authorization method was half done and ugly, it was a big BIG monolithic method that did everything in a way that either past me or current me like at all because it takes a whole lotta things to work (8 to 9 argumentos lol). In the end i just splitted the big MONOLITHIC (thick) method into two of the same kind but with at least clear and defined and bleh jobs. One when the user has already authorized the client and the other when he has never touched it in its life. I also did some qol for my self (or i think i did, probs not because it was already bad and rushed and stuff… i already said it at the start) with the oauth helpers and stuff by also prettifying them and simplying parts that I really don’t know why I added. Like I added mr “OauthResponse” which, as the name implies, makes the oauth response (redirect, plain error, error with redirect) instead of the 3 different methods I had to use to do the same thing, easier is better (and even understandable!). Ah, and i even tried to use strum but nope, it do not cut the job (it tries to cut the wall instead of the thing i want). NOW, sHALl the work continue to make the token making handler and then the OpenID stuff that may be a bit difficult because, with my shitty reading, i think ill have to juggle making jwks and stuffies to server correctly the food for the picky openid. here’s the video that shows, confusingly, that indeed the handler works (It does a “hi, oh you want to authenticate with ‘poop’, let me create a pending authentication. Oh! you accepted the consent, let me delete the pending one and create the actual lasting consent for ‘poop’.). god, i am so bad at coding or whatever the this is:
you know? i love debugging my code when the error was the entire time in the db. great job me!!!! I’ve made the token handler. again, as other devlogs, that’s all folks. Stupid me couldnt figure out why client secret was always bad, why the mr code verifier was always bad when it was the same as the stored one aaand why the heck scopes werent showing. i really do love that my code, practically, worked first try (MY CODE >:3). But did NOT love that i am still stupid and forgot that i had a database that works as the house for the data that my app ingests. god i am smart. time to go suffer with openid i guess. i have to touch jwt and stuff, eugh.. i like my opaque tokens more than jwts, because IF you want to revoke one of them you still HAVE TO STORE A LIST OF IDS… that defeats the whole hecking purpose dude, not worth it (or that’s what i think for now, smh i am just a space birb claking at keys). here’s your video that shows that it does work and gives the bearer opaque token and creates the db entry:
oh hi, did not know that you are really reading this. I added the openid stuff! Well not all of it, I still need to do the ‘userinfo’ endpoint but meh, it works as it should. I also added the cool JWK (the secret key [ES256] that is used to sign the JWTs) rotation stuff, which I can say that I tried at first over complicating it to then end up simplifying it because it was A HELL to use like you had to call a thousand hecking things or in one method give a thousand arguments (its an overestimation, probs not real [shy of 5, it wasnt generic, it was attached to the token flow] :3), it was a pain to use and IT EVEN GAVE ME a burn mark. As I said, I just redo’d everything and boom, simplified thigny you just call from the global state and boom, we do be working! The JWKs are rotated every 2 months, and the old one is retired at the same time. After 1 month, the retired one is deleted. So, at any given time, there’s a maximum of 2 keys, the current one and the retired one. The retired one, as the name implies, is not used to sign new tokens but is still valid to verify old token. And all of this is fetched at startup and then via a scheduled job it is updated. AND as you might have (NOT) guessed the JWKs are stored in the database, so if you have multiple instances of mah meow meow server running, they all share the same JWKs and rotate… OH GOD THEY DO NOT ROTATE THEM AT THE SAME TIME GODDAMIT AAAGH.. the intention was there okay? I did that to, in one part, give more use to my job manager and, to let all isntances update at the same time but big brain me did not think that a job is always locked to the instance that picks it up. also the token endpoint now supports using openid and spitting out a ‘id_token’ with the user info (some claims are gatekeept behind the scopes [like ‘profile’ or ‘email’ duh]). that’s it actually, like i started really pulling my hair, fluff, feathers whatever because I was trying to overcomplicate it a lot and i really dunno why. like, for another example, i kept swapping from the ‘jsonwebtokens’ and ‘compact_jwt’ crates because I wanted to use ES256 but didnt want the other because i never used it but then ended up using the latter because it was easier, and then that I wanted to use EC25519 which is apparently not really talked about in the rfcs (that’s why i ended up with ES256! [also supported on compact_jwt]). AH! and also in the job queue, for the JWK rotation, I wanted first to add cron job for that but… why tho, I don’t really need it, I just need a way to NOT run a job until a specific time and then reschedule it inside the same worker. here’s the shitty screenshots showing the oh wow ‘openid configuration’, oh wowie ‘it does output tokens and id_token’, oh thats nice ‘it does give out a valid jwt with valid claims that the programmer didn’t even do themselves because they used a library called compact_jwt’ and wow ‘it does have a job that tries to autorotate if the current key is older than two months’:
I made the User Info handler for the open id stuff. that’s the end of it. It works, wahoo my self yay good job, even though you had pretty much bad scrambled code, you did it hahaha. It practically, as the openid spec says, returns the claims used also on the id_token. Soo, because its sooo close to the id_token, i’ve decided to just eat the that same id_token but spit out a converted version with things I want. I also added the typical OauthManager and RequiresOauth. the latter one not having a param to decide what scopes you want because, whhy would you want that?! its just a one liner and it may change for each hecking endpoint or even the data the endpoint returns. its just over complicating it with wrapped trash. … and about my devlogs, i realized that mine are pretty bad and not interesting like other people’s devlogs. i simply write weird and long ones. but i don’t want to write a simple “I did this, commits, hardness of implementing, whatever” in bullet points like other people do. i dunno it feels flat like this devlog. Here’s you typical SINGLE screenshot showing that the user info works, that’s it:
hi, hello i am space birb in the sad corner and i am here to tell you that i added the the typical oauth management handlers that everything has. I also added a generic paginated database thingy to be able to make… pages when there’s more than 20 items (rows). firstly, everything was easy. yes, no rocks not nothing that could set me on fire… minus that I HAD TO MAKE a paginated database thingy. WHY did i make it? idk man, i wanted because my bad coder brain thought that maybe someone could have more than 1 oauth application created (like a thousand) and that’s pretty bad for mr blue elephant database becuase that means eating 1000 rows in one go (and blue elephant doesnt like that, AND EVEN less if my vps has 1vcore and not really that much ram). SO, the best course of action was to make pagination to be able to cut in pieces the data and make it easier to load and handle.. even though the loaded rows are hardcoded (its 20 plus 1). The pagination practically works by seeing if there’s more data after 20 items (that’s why it has a “plus 1”!), if so, we take the LAST item’s id (using a trait to access it, because you can’t really know in what form or what id will be used in any database model) in those same 20 items to set the ‘next’ param. That ‘next’ param is used to.. quite literally say “hi, uh could you give me the next rows after this id pretty please?” and boom works and i didnt set my self on fire but i did ruffle my feathers a bit because of. Ill probably implement it to more places because the only places that use it are the oauth applications and user sessions because i am a bit lazy haha (i mean, its just C&P the current find_many* and use the pagination stuff and caboosh). oh god, i forgot about the oauth consent management. great job me, NOW IT ISNT COMPLETE “everything” great coder i am great big bird brain. My next plans are just to do some general cleanup with the openapi schema, add comments to the code and then start doing the frontend in mr sveltekit.. although i am too rusty in ts land and i will have to learn some stuff again and touch again scss and stuffies and god, i am going to get cooked in the sun and roasted like the space birb i am because of me no longer touching anything javascript. here’s your really useless screenshots showing the INCOMPLETE oauth management handlers and the pagination thingy working:
hi and welcome to my unreadable journals. I added those oauth authorized applications management handlers that I forgot to add yesterday, like the typical “oh let me see what scopes does this thing use… OH GOD IT CAN SEE MY fur color I MUST revoke it NOW!11”. Then I moved AAAL the handlers that touched ME user data to the.. obvious /me route so its prettier and doesnt pollute the air of the real auth related things. and did some cleanup in the http error department by merging some repeated errors. god this is one of the shorter entries i’ve made i think. next up is doing ratelimting, probably roles or just an admin api and the “hey your account has been locked because of repeated tries to login”. i dont even know what will it take to make tests work god. here’s your shitty screenshot showing my only progress, the oauth management bleh bleh bleh:
hello again, its a me, a random space birb that is here to tell you that I completly forgot about the ratelimiting stuff. BUUT, I added the admin methods and UserFlags to be able to set… flags. not much rodeo this time. had to think how i wanted the audit logs to work for the admin stuff. I first though about just having a bool saying “ah yeah admin” but meh, I think its better to have also an “actor_id”, the who did it user id, and boom. In the user side handlers for the audit log I just return the typical plain response of “yeah this was changed” (not sql joins, cheaper [?]) but in the admin side, I return the actor_id with some other things like the user login of the actor AND the user login of the user that was changed (cause why not!? its jsut another join statement in sql). Works wonderfully and even better now because before I had setup the user audit log to fetch AAAL the entries form the db and pee pee poo poo no, now that i have my paginated stuff, no way! SOO, the admin methods are just the typical ones, see what the user has changed or an admin changed from an user via its audit logs, the oauth applications and authorizations management and that’s it. I will probably also add a way to know globally what the heck is being changed but idk about that, seems a big sql query (that is tiny but very costly in db terms) and I frankly want to avoid that… and even more avoid using redis because i hate it and that SOMEHOW i have better some beautiful organized code vs the first version of this backend where I used the typical DB+redis aaand it was ugly . i hate that. I also added the UserFlags to be able to set flags like “is the user an admin?” or “is this user allowed to manage its oauth applications?” and so on. Pretty cool. BUUT as always, I had to C&P some code (almost all of it) from past handlers to make the admin handlers work lol. I would really want to make a central place to put all the common handler code but meh. IT WORKS! and that’s what matters. THe next things are just making the ratelimiting WHICCHC probably will be only inside the instance the user is reaching and that’s it. I would like to have global ratelimiting using the IP of the user or its account id but that would (i think) require redis… I really need to think about this because someone with a crappy toaster can make explode my poor baby vps. ps. god do I really take that much time doing stuff? god i am slow at coding. i am bad goddamit. that’s it. shallllll you see my shiitty api docs with the admin stuff and admin auditlogs becuase why not:
wow. that’s a long time doing nothing. I just updated the deps, tera had a change on its templating because it was rewritten from the ground up. pretty cool, now it kinda looks like jsx and at the same time not. I also tried or well… I am thinking about making my own ratelimiter but was like, ugh. I was practically mega blocked on the idea of wanting a ratelimiter that used the IP + UserId. Both are easy but for some goddamn reason I kept going circles and circles and honestly I dont even remember why anymore, like huh!? In the end I will only be doing ratelimits via IP because they are the easiest AND THEY DO NOT NEED TO be stored in the database. yes database, you read it right. IN this version of my backend I wanted to use the least things AAANd the oNly thing required is just the database to make everything. Adding redis would be waste (and waste of ram) for just storing the ratelimiter. Redis would be required if I wanted to run more than 1 instance of my application AND IF i wanted to distribute the info about “oh this guy is grounded, no entering the meow auth realm for 5 minutes” to all other running instances. BUTTT, as you might not see, I dont want to run more than instance nor want to distribute it to more instances if it were the case that I wnted to run more than 1 instance. Soo, I went in circles in my crapppy bird brain because to add ratelimits via the user id I would have to store the info somewhere that wouldnt be erased (like in the in-memmory storing that i will be using) or else the ratelimits would be useless because… THEY WOULD BE ERASED from this whole realm when the instance is restarted (memory goes boom to the void, so does the ratelimiter). That’s why I want to just implement the Ip based one, its the easiest one and the only one that I don’t care about loosing the information about the ratelimits when the instance is restarted. Also, i need to implement it yet lol i was just scrambling my head nuts and bolts hearing them clank while trying to ratchet them. ALSO², i need to redo the email stuff because my brain has came up with a cool design lol but first gotta do the frontend to make it uhh look good before suffering making the email components. I also started to hate the tera zed language extension. its crap. bad crap the worst one. ALSO, here’s your really useless screenshot this time showing code that you can already see on the github page (yay!1):
right, its a me! the teeny tiny waist sized space birb that is here to tell you that I added the ratelimiter! It works just as i’ve explained in the last entry, it uses the IP of the user to limit the amount of requests via a ticket bucket. THe bucket is stored in memory, as again said before, so if the instance is restarted, goodbye ratelimits. EAch request eats 1 ticket AND the bucket tickets and refill rate can be set via the… middleware. who would have known wow. It always adds the typical “x-ratelimit-limit” bleh bleh bleh with all the info you can like that is useless BUT hey, its there for you. I like making my own stuffies because its cool. THe root has a limit of 500 requests, refilling after 1 second. While the auth routes have a limit of 10 (sudo) and 20 (auth flows) requests, refilling after 1 minute. The other routes are just uhh protected under the umbrella of the root ratelimiter lol, no need for having to add a ratelimiter to every single route (god). that’s it. there’s no more devlog. Now what i gotta start doing is the frontend. I will cry a bit because I am VERY very VEEERY rustified in the typescript department like I never touch it but meh I MUST do a frontend or else this is incomplete… and i will do be crying if i send it off in a incomplete state. THE frontend will be in svelte kit, because its cool and ive already used it in the last iteration and it left me with a good taste in my forked tongue (hahaha, i am space birb) AND thanks that Ive been using from the start utoipa to add an openapi schema, I can use it easily to make requests from the frontend! and i didnt need to suffer adding them after everything was done just like in the past iteration! here’s your useless screenshot showing teh ratelimiter working and the headers and stuffies… god i am a mediocre at best writting long hecking devlogs for nothing:
oopsy. shoooort one, sorry. I added the forgotten oauth error StatusCodes and Descriptions. there’s not much to it lol, I just forgot about it. as always, here’s your even uselesser screenshot with code taht you can already see in the guthub:
a. hi there mr svelte! I was wondering if I should be drafting first a design in figma but meh. I like to suffer and make bad designs that progress very slowly to look mediocre at best! yay!1!! anyways, ill be using scss as always (i frankly done even remember anything about it lol) and sveltekit. HERE’S YOUR stupid “hello world” from slevetkt (reaaly useful devlog):
hi hello. oh, ive been 10 hours doing the same thing!!1 god. So, I was doing the frontend design without using figma and uhh yes. And also removed the ability to use your login to.. login to the app. You just use your email (otp, and passkeys). First, I forgot how to use figma, I only remember some shortcuts. I was going to use it (for frontend and logos) but seeing how struggled to make simple things, I only used it to swap the colors of the logo of MEOW AUTH, hmm yes, very meow. . The frontend stuff… I frankly ALSO do not remember anything.. or well I do now remember some things thanks to me fighting to uncenter a div hah. The stack is practically svelte, as always, client rendered and scss (sassy :o) for the styling. In the start I was just going to use the same styling and structure of my first iteration of this project, beepauth (ew), and after doing all of that I thought “god, this looks pretty bad”, soo I then tried to make it look a bit better WITHOUT starting from zero again. And I did style it a bit better but I didn’t even like it. Like it felt like I hadnt even advanced at all, like if the same shitty fricking backend was the same from the past (probably noone would care because probably uhh noone cares.) [brains a weired and even more mine i guess, wawa]. So, naturally, i did the less sane thing, REDOING EVERYTHING again. Had to watch tutorials to relearn how use sass and picked up also some new css stuff that I wont even use (minus the layers! that pretty cool!!). And from that css stuff I discovered Oklch.. yes i swapped my whole color palette to that using their harmonizer that uses the equivalent of WCAG, APCA (contrast bg to text!). At least the frontend now looks decent (or the first blocks fo the website). sadly the only frcking shitting heckign parts ive made are the login (and register BUT THATS THE SAME). AWIT WAIT WAWA, I also swapped some gears in the BACKEND! I removed the login (username) entirely, now its just demoted to a non unique display name (for openid!). you must be asking why, because I changed it to use the login not long ago. Well, because I don’t have to force a user to give me a username AND their email on signup. Its easier to just give the email. And if you already have an account and hit, without knowing (our typical autopilot mode), the signup page, I can silently make a AuthChallenge for login instead of signup because both handlers return the same result. Then, after registering, I just force you to set a display name (by default its just the email, i could leave it like that but currently I want it to force you set a custom display name for OPENID). By doing that, I practically removed the “vectors” (ooh hackiry stuffiry) for user enumeration [those vectors were the endpoints to see if a login (username) or email was already taken] (I also made so the login endpoint gives you a fake challenge id when the email doesnt exist {This works thanks to having the auth exchange handlers return “invalid code” even if the challenge doesnt even exist}). here’s your screenshot of a login page that looks exactly like very other login page, maybe even shittier (sorry i am bad doing these kinds of things.) [also you have the logo colors, i went with purple because it looks cool and its a cool color] {the “Psst, over here” its just a random greeting, just like the ones i have in the emails} [[sorry for wasting 10 hours doing nothing]]:
i am ashamed. i am that bad or… well ive gotten to the point of real badness and what some call “skill issue” because I CANT make this hecking frontend. its just too hard, i am just way more slower than before and what i though i was. i hate that i am not usign tailwindcss and isntead I am building my own scss styles IN @layer when i could have been using svelte components with the style tag but ugh bad decisions that can be still reverted i guess. For the layout of the settings, i even tried copying how I did it in the last iteration of this frontend (past server, not named meow auth).. i just did not like it because using a sidebar for this teeny tiny amount of information is wasteful (like this tiny [not really tiny] space birb). Like the content is just the typical name, email, auditlog, 2fa, passkeys, sessions, oauth authorizations and user created oauth apps AND lets not forget about the admin panel. i am practically suffering with mundane crap that anyone can have done in probably 10 minutes by asking mr claude or even chatgpt OR even just copying an already made layout, but here i am suffering trying to make stuff my own way, telling my self that this was the worst decision i’ve made goddamit… at this point i will really become a hotpot space birb soup.. i just need one layout made and then it should be an easy job to then copy and paste to other tabs. then the enxt step is to wire it up the forms and have it communicate to the backend which should be an easy task (i think [… oh god please be easy])… i know this entries of the frontend will make my project go into deflation mode and stuff, and i even broken a promise that ive made my self to not make devlogs that are more than 5h long because of a review someone made of my project when i submitted it unfinished to flavourtown (yes, flavortown) which is sad but it is what is. you cant be perfect, that’s why i have a shit hecking design. i am just bad, a bad space bird i guess. i already rambled too much in this suppossedly simple devlog. here’s your half crap “inspiration” that I used to (quite literally did a C&P of the page) of my past proyect using a sidebar (not what i want with this iteration) and the current state of art of my REALLY REALLY SHITTY curernt state page:
in the end i gave up in the design or well… can you even call it “design” when it was ugly even at teh start? yes, i tried my best at making it look “good” in my own eyes but i could not for the love of birds. it was bad anyways, like actually bad. I, to make it easy for my self and be able to copy how other people do it because i am a lazy bastard (stupid space birb that klanks its keyboard for nothing), will be using the typical two columns for each section. One for “what the heck is this section for” and the other for the actual content (wow). man, i hate how the frontend is where i struggle the most when the backend should be where i should have struggled the most, not with this dude. agh. I also hate HOW i thought the layout i wanted initially was the best one and that i look great when in reality it was an aberration that didnt even look good and it really threw to the trash the unused space bruh. it just wasnt for this job, maybe for something else.. idk haha, what I know is that I think it already looks better with the simple sutff ive done lol [that’s sad btw :)]. as you may see int eh screen shooting, the audit log section instead of having a table it uses a… placeholder for a button with the same content it has currently because I frankly i am scared of making tables because of the probable difficulty i will have (because I am already struggling with teh simplest of things!). ALSO, i need to stop making .scss files, compounded into a single css file using @layer and @use , to just use svelte components that use the style tag. it really doesnt change anything, but atleast i wont be doing “class=‘meow’” each time i want something, just using “” is half better lol (doesnt really fix anything, I just want to give use the svelte components). finally, i am planning to use “services”.. they arent really services but more about wrappers around the openapi client. less manual labor for some shitty stuff that i wont need to do anymore. Still, its just a wrapper with nicer method names and namespaces. (also, ill be adding profile pictures. didnt think i would be adding them tho) here’s the typical dumb screenshot of what i think that looks “good” without doing that much work:
haha 10 days later. no progress just me rambling and going in circles. I am a bad space birb. RIGHT! I just were converting the css stuff into svelte components using the style tag (and the preprocessor duh). That’s actually it, quite literally. No progress at all. Wasted time on, still, falling on the loop of trying to get something that “looks good”, but i dont reall know what i want in reality. ofc i try to get “inspiratainionion” from other websites and try to mix them together to then dislike the result, undo and rinse and repeat (at this point my fur and feather soaked hard on water). YOu may imagine it as thinking that you ahve a finish line when you done even know where it is. i will just try to make the frontend work first without thinking about the design… AND i cant even promise THAT! because i will probbaly spiral into the same loop once again (saying oh this looks bad blah bleh bleh). god i am bad at making frontends or evne design or everything, but thats how people learn haha. it is a lesson that i should learn from now on.. i keep judging my work too harsly but i dont know. frontend is just harder for me. here’s the useless non functional three pages that atleast have something layout:
would you look at that! Finnaly some functional pages with forms, ALmost finalized components… oh they are just the login pages….. and its not even all of the auth pages, just selected ones so the auth flow barely works. BUT, THIS fLuffy (not fluffy) space bird has finalllizated converting (or thinks) the classes i had as global styles into beautiful svelte components with all those stars and happy things. happily, i am longer limited by having evertyhign in global space.. oh right, RIGHT. i wasnt even limited :). AND the coolest compponent of all of them is the spinner one because it spins. The first pages have been plugged into an outlet called SuperForms (not really super, mostly helping to validate)! thanks to that, we now have working forms that post to the backend! with all those classic things, red colors and form validation. but, how I THE unfluffy space bird is pushing envelopes with the form inside of it? by talking to my backend obviously! but not manually (doign every step each time manually), by using a library like the trusty openapi-fetch that eats the openapi schema of the server i used in the past iteration. BUT i trashed it into the.. trash because I was fed up by having to repeat a thousand times the same snippet of code in each form or even page (I could have used a service like class to make it easier tho!). it was swapped by Orval! Orval works with the same openapi schema BUT gives methods! No more repeating the same things, just use the method and boom. and also that openapi-fetch was mad that i used a more recent typescript version. ANND, finally, the auth context was upgraded with a leg made of clay so it can now parse the BITENUM flags of, for example, the user (UserFlags) that tell what is enabled and what is not, if you didnt remember. Its practically a generic class with everything that i considered necessary that must be extended to be able to use it. and that’s it. you may watch the shittiest video ever that just shows that it kinda works with the forms and logging in and that’s it with still mocked things:
well hello again. space birb presents you the next devlog. I cleaned up the forms a bit (they are a still bit TOO messy ugly but they do work), REDID the ugly flags class to be a more EEEASIER TO WROK WITH (i dont even know how it worked before lol) and FINALIZED the auth pages! RIGHT right, first frontend. I finally finalized the most basic and empty and everything pages of the frontend! wahoo mee :). these pages practically include the otp one and the 2fa one (+ recovery).. and i just realized that i forgot about the passkey one, great so now it isnt finished (fried space bird thingy). Redid the pin component so it works with my Input form component AND also fixed some weired quicks of the pin component in phones (for some reason, my phone, didnt respect the max lenght of the input. i just sliced the value of the input on my max length lol). And finally, the frontend now has the most precious bit of any… i dont know. i just added the typical dropdown componetn for the avatars (yes, this was important for me). while on the backend, because mr past moon made some weired decisions, the 2FA recovery code generator and giver of the code the state was a messy mess that I frankly dont know if I even tested it, because it accepted wrong codes? i dont even remember if it worked haha in the past BUT now it works as it should and its at least better readable with normal method names (not plainly “recovery” [… what does that mean anyway!?? you would need to look at the code to understand it! and that’s bad]). ah, the typical moment has arrived, the one i always repeat. HERE’s your happily working auth pages for once, plus the “want to retry the email sending?”, the passkey one is still missing but that’s just slapping some paint (and painting my self) and boom (whhile this is practically the same as the past devlog, it just works better than the past devlog lol):
would you look at that, 10h… i love wasting time. Now THIS thing fetches some bits of data like the audit log, my passkeys, adding those and even modifying the username (you are forced to do it)! BUT big buty, i had to add the mr /signup/complete where you set your prefered username, like moon (its a me!), or else the backend will straight up refuse anything you want to do. AFTER changing it, you can do everything else a normal user can [by Default the username is just the user’s email AND THAT IS NOT PRETTY AT ALL >:(] FIRST OF all i grabbed my magic cup for tea and made the audit log page (it does not even have anything special)! .. oh right, for that i needed to know how the heck you even send requests with tanstack. so uhh,, blah bleh bluh wawa BOOM i now know how to call a method and get my data back AAnd even make infinite fetchers (IT kinda hates my way of giving data [its wrapped in {3} onion layers {request.data.data.data}, the ones that make you cry a bit]). i also synced my auth state with the tanstack client so it doesnt do two trips every time. okay now. TO make the audit log thingy, i did some beautiful matching so the type, creation and guy who did the action (probably the user) are actually human readable with a limit of 8 items (or else the /me page would be as long as this project’s crappy devlogs). When there’s more, a beautiful “view more” button appears (separate page). For this, so called “view more” page, i used the infinite queries. while at first i just puuushed the data into the dom with the #each (svelte thingy), chrome do be VERY very mad when there’s a lot of entries in there (The audit log, because it logs actions in your account, can get very huge. Because actions like the baddie Session action logs each time you log in or log out), SOO i did what someone whose browser is eating more than 10gb does, using a virtual list to render the it! (IT only renders items when they are visible, hidding [removing from the dom] non visible ones. this fixes the “OH gosh THOSE 200 things THAT I CANT EVEN SEE, ARE EATING MY precious ram”). OHh and you can press ESC to leave the page because i made a cool component that lets me plug in a callback for specific keyboard events :D. SECOND, the passkey stuff. yeah, pretty easy thanks to libraries like SimpleWebAuthn that do the hard work of telling the browser “hey, authenticate this guy please” or “hiya, registrate this passkey please”. its just querying what passkys you have to know… hwat passkeys you have and that’s it. BUT sadly, an actually sadly, my passkeys are non resident (I cant have a simple button to just login, I need you email before i can use the passkey). Thaaat means I havea teeny big tiny small user enumaration problemo becuase the backend can reveal that an user has a passkey registered and, in turn, we know that user is registered. a bad guy can just atack with “oh noes, your passkey has been stolen. pls give a undecillion of moneys”. third, i had to add the sudo page. It does what you think, reauthenticate to do sensitive operations like adding a passkey. Pretty simple, it redirects directly to an email code if the user does not have a passkey or totp enabled (email code is always enabled), if they have those, it just shows a selection screen. fourth, i had to fix up some stupid things with the api schema of the backend becuase I either forgot to put a body on the responses (like the passkey/webauthn routes) or just fix up a teeeeeny tiny setting thing that wasnt correct (its the webauthn/passkey rp_origin [allowed pages to register/use a passkey} becuase it jsut used the backend url and THAT is not correct). so tahat’s it, shall you see some stupid screenshots and not even a video because i havent even finished everything:
hi hello this is space bird. i have eaten 9h hours of real world time in just making the dialog component. I mean, IT WAS for making the 2FA prompts for when you wanted to enable it or disable and bleh bluh.. atleast it does look great… and i still need to add icons. the prompt is just the typical 2fa enable that gives you a qr or the 2fa secret if you cant scan the qr. The dialog component was pretty much made in nothing, in air time (free :D). the thing that took a bit was just the styling BUT WAHT did take a BIG LOOOT of time was just making the 2FA dialogs. lik gha damn hecking “2FA prompt” and “beep boop enabling dialogs”.. why?! becuase they are PACKED like a lasagna with forms, tanstack fetching, dialog steps and other stupid stuff to make it atleast not confusing for the user to navigate with tree sized buttons that dont work (becuase you are on the wrong step!!!!1 or already did everything lol). i mean, besides that custom stuff, it just reuses components like the pin or qr svg and… for now, also uses the spinner icon on the buttons you click to manage 2fa as i couldnt be asked to add real icons for now (leaving them for lasts). next up working work is more dialogs (for the passkey setup [so it isnt just a spinner without any “magic guideance”]), a collapsible comp to wrap the info items to show EVENTUALLY some metadata like, in the session items, browser, ip, what user was that did that thing and etc. Its just so i dont balloon the “info items” BECaUSE they are supposed to be some kind of “quick info”. broom brroom, i love wasting time on these stupid things (and wiring them!) >:3. LEAVING that tiny goofy work that apparently took hours to do. I am thinking and WILL be doing a beautiful swap into using resident passkeys (the ones that dont require me to know the user’s email in advance). yes, i know i said before that i dont wanted and ew ew resident. but man, its just easier for the end user to click a magical button to login directly. AND AND this even protects me from someone slapping the meowbackend with the email fish to see who is registered (with a passkey) by checking if the user’s email makes the selector appear. still, some part of me still thinks this is really bad idea but i want to do it tho… what could go wrong hah! i might aswell go to the cooking pot already and make some uhh chicken soup made of space birb. that’s it, i still feel like i havent done anything. Here’s your beautiful video of showing the beautiful dialog going “hi” “goodbye” (the video shows me enabling 2fa, viewing recovery codes and disabling it [with a recovery code]):
hi hello hiiii, stupid space birb back from long bed sleep break. CATCHING UP, i have swapped to resident passkeys, fixed jwk refresh feature not working, beautified toml settings, added 2fa replay protection and audit log beautification. alright.. prepare for a long boring devlog as always. I finally swapped to resident passkeys after thinking about it for days (hours)! That means.. NO MORE user enumeration problemo with passkeys, yippie. it wasnt really a pain in the feathered buttocks, like it was just enabling a crate flag and boomy boom (and it even works on the frontend!). also, i patched with scotch tape a stupid buggy bug about the email templates, becuase I used a lazylock to store the email templater engine stuffy, when SOME random goofy template or data was wrong for whatever reason, it would poison the lazylock and the next time it was used it would just say “oh noes, i am broken, poisoned my childs. GO HECK YOUR SELF”… i just fixed the template part by checking if tera likes them in the global state creation. i love scotch tape, it smells great. while doing that, i finally addressed the beautiful jwks not updating on all instances because in MY JOB QUEUE only one running server instance can eat the job. that means, if you have 2 instances running, the one that is the unfortunate one that didnt eat the job will have stale jwks until the next time it is restarted. fixed it with some beautiful blue elephant database screaming “HEY GUYS, THE JWKS HAVE BEEN UPDATED” and the other instances will be like “oh, okay, i will update my beautiful jwks now”. finally, 2fa, or as i like to call it, TOTP, has been taken cared like a baby and then thrown again to the battle field after giving it the power up of replay protection. That means, if you use a code, it will be marked as used and you cant use it again (this only works with valid codes). makes you wait more smh. THE SETTINGS got a layer of microscopic makeup because i was like “man, these setting name are weired? what if the settings file had comments to know what the heck is this beautiful knob named ‘do_not_touch_this’”. yes i did that. it was a big red booty pain as i had rerereremember how to do proc_macros and how the heck did i even update the toml files with new entries without making them implode with gibrish. AND WE ALL KNOW NOW that ‘do_not_touch_this’ is a setting that if you set it to true, it will make this space birb implode in flames and become dust (yes, like a real firework going CHIRPAAAAAAAAAAAH pop ). and the final thing given some body work spray was the audit log. no more caveman moonbeeper style ‘log’ method with a thousand things. now we have the SAME thing but as a builder with a ‘save’ method. it just makes the code easier to read and understand. also, the audit log now has a “what resource¿” and “what ip?” and i removed the audit entry making from inside the auth helpers to outside. it was just an inconvenient place to put it, i dont evne know why would moonbeeper put it there… oh right i am moonbeeper. I DID IT BECAUSE it was a great place in those times. alright, HERE’s your typical bad screenshots that are trashign and god i am sleepy:
pew pew cabosh, wawah space birb is very sleepy. BREAKING NEWS, i added and changed a lot of things so i could make a delicious soup of changes that changed my old changed code that i yes. minimal frontend work called making the passkey section button go crazy with colors, authenticator icon (just search “aaguid explorer”, its not crazy work) and having a dialog telling “hi do you want to rename this, no? okay”. I also have collapsibles that hide info from your lil nonfluffy face when its useless. Now my crappy server knows from where you are connecting (who would have known that a server could know that) and where you live (oh no [its approximated from your ip]). THe latter only works when the instance is proxied under cloudflare (it even gets mad if a request doesnt have the headers it wants when you have enabled the “under cloudflare” mode). the audit log single method to do everything™ has been swapped with a builder (i love this), AND its info now includes that same ip and location (actor related [who did it]). all fields are now obligatory for… audit log purposes, because before i just pushed null on the db for things i didnt want to show the user MAKING a pretty stinker audit log that does not show who did what. i really dont know whata past me thought, because you can keep everything on the db and just show what you want to the user. idk, past me thinking in uhm not portals (yes). okay, more slurry of things. mah beautiful fix so the JWKS (signing keys used to… sign a jwt! [important for OpenID]) update when the database JWKS changed, spammed the hell out of the db and log as it polled instead of waiting for the db (or an instance) to say “I UPDATED IT GUYSS!1”!1”. This comes from the instances cache’ing JWKS instead of saying “could you repeat that” to the db (too many requests = blue elephant cry and explosions), so they need to know when the db changes or THEY WILL BE out of sync!! mr past me also thought that it was a good idea to store the oauth token code (used to get an access token) in plaintext in the muffing db, SMTH YOU SHOULDNT AT ALL DO. now its just hashed via hmac just like everything else. man i am GOOOD at this job of so called programming, having to go back to edit a thing i called done AND then HAVINGTO edit and edit and edit. this is so fun that I WILL BURST into FLAMES because of how stoopid i am meanwhile, you also have me deserving to be cooked with rice because I made a condition in the oauth token exchange that when SOMEONE somehow miss types or gets access to the oauth token code but has the wrong client_id, the temporary code gets deleted… and you know what i forgot? i forgot to commit the changes to the db, so the code never was deleted lol. i love these kind of stupid goofy pain errors that arent even errors. AALSO I reprettyfied the oauth helpers because after working on them a long time ago, i didnt know what were they doing and it wasnt fun nor funny >:( [i was mad {i am still mad, with that space birb… i want to sentence past moon to sad corner jail for 12 seconds}]. NOW teeny tiny additions!“1 the ratelimiter now exposes a “Retry-After” per a hacker news old moldy article i found, plus exposes its headers on cors. the audit log now does its job even in more parts! now it also tracks the totp recovery code usage [i frankly forgot about it :(].Changing your email or name now requires sudo minus the name on register. AND the app startup has now a beautiful banner saying “meow auth” in cursive. its pretty :3, makes it stylishinning. I also moved and added to the service manager (the watcher) a way to have service names and to launch services from inside of it (for some reason i used a method that was outof place for ages lol). oh no my time has come nnonono, i give you permission to see my trashy work in screenshot format (so modern)… yet i shall push towards completion of this meowing meow meow authing meow auth server:...
|
1792738089.8583465
|
1793342889.8583465
|
|
20497
|
ScreenExtend by @epiccodewizard | Stardance
|
epiccodewizard
|
1
|
129.27
|
158
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-07-01T23:47:06Z", "hours": 55.0, "multiplier": 15.76, "payout": 158, "blessing": "neutral", "text": "Initial release for ScreenExtend! Please give feedback and read the initial devlog (shorter one here, longer one at https://github.com/ScreenExtend/ScreenExtend/blob/main/DEVLOG.md ) for detailed information.", "repo": "https://github.com/ScreenExtend/ScreenExtend", "demo": "https://screenextend.app/"}]...
|
[{"post_id": 60703, "ts": "202 [{"post_id": 60703, "ts": "2026-09-03T08:05:38Z", "hours": 9.5622, "text": "One step closer to finally shipping…or so I hope. I speedran shipping this next feature, so it took a lot of time. This feature is mainly for myself. I have an old Mac (running 10.13 from 2017, really old I get itttt). I want my app to run on there, but it currently only supports 10.15+. Hence, I went down a funny rabbit hole in an attempt to add support. 33. Cleaning up dylib signature lookups at run time One of the key challenges I faced throughout development was how to have a unified codebase for multiple API versions. I decided that instead of version-gating APIs and guessing which beta/normal versions they would be accessible on, I’d test for APIs at run time. I changed VideoToolbox to use this logic: it would attempt to set/use APIs and variables available at higher runtimes, but would work fine without them and have fallbacks. This ensured both legacy and modern systems functioned well. 34. Airplay automations…? As I was doing competitor research, I noticed many of the other apps supported Mac 10.13-10.15 as well as 10.15+, while mainly only supported 10.15+. Essentially, they started a fake Airplay server, had the Mac connect it, and set the display mode to “Use as separate display”. This would create a virtual display that the app would then use for its own functions. As ScreenExtend requires its own display on older machines, similar to this, where CGVirtualDisplay isn’t an option, I decided to use this method. I had my app automate opening the Airplay menu and connecting as well, in addition to running the fake server. Most of the process was straightforward, especially due to the abundance of fake Airplay server implementations online and my own experience with apps like LonelyScreen. The one issue of this approach is that only one display can be connected at a time, so any tertiary devices wanting to connect have to wait their turn. I guess the only option is to by a new mac then… 35. Client UI update cleanliness fixes Due to the one device restriction, I implemented a separate page that would be served if the display slot was being used up. It would be coupled with a short message explaining the consequences of the older Mac version. There were also bugs related to session teardown (keyboard still being locked, video stream still active in the background, etc) that I quickly squashed after finding them. Soooo in hindsight, I prolly should’ve bumped this up to 0.6.0, but I left it at 0.5.4. Oh well, I’ll increment the version by next release. Only a few more features to go on the feature list before hitting v1 and shipping!!!"}, {"post_id": 58576, "ts": "2026-08-31T01:22:07Z", "hours": 12.0222, "text": "Oh my lord, I keep forgetting to write devlogs every 10 hours and am losing hours on each one. Nevertheless, I finally finished audio work! Plus some other things like running claude security agents on the code base and patching stupid vulnerabilities. Here’s how the week went: 28. HiDPI and Mac Issues Whenever I use my Mac with ScreenExtend, the stream looks blurry due to Macs having HiDPI screens. I fixed this by adding basic HiDPI scaling on Windows. The UI has a DPR slider based on autodetected values. This DPR is multiplied by the screen resolution for a shaper display. One issue that arose with the new displays was the fact that the display would randomly not scale properly, so the task bar and apps would appear tiny. To mitigate this, I reapplied all display settings after resolution changes and a timeout. 29. AUDIO WORKS!!!! For the longest time, I tried getting audio to work. On Windows, the process was straightforward: a WASAPI loopback system audio capture session. I wanted to build my own custom driver to capture audio faster, but this would be too expensive to deploy. I read other Rust implementations and quickly wrote the code. On the other hand, Mac was a whole different issue. On older versions of Mac, system audio recording is not supported. Hence, for Macs below 13.0, I wrote a custom audio driver. I didn’t have a clue about implementing an AudioServerPlugIn on Mac, but worked out the specifics by looking at the Blackhole audio driver. I did have to proxy physical volume buttons to work (i.e. I listen for taps and change the audio level within the driver). For older Macs, I used ScreenCaptureKit with an extra audio flag. This was a simple fix to capture audio. On even newer Macs, there’s a Process Tap API to record system audio with low latency. Once again, I didn’t have much difficulties implementing these due to the abundance of documentation. 30. Streaming Audio over WebRTC (with A/V sync) When sending video and audio separately, the two are prone to fall out of sync, so watching movies or videos is difficult. To mitigate these issues, I wrote an A/V sync host clock. This packages the time on the host clock to audio samples and video frames, such that they can be reconstructed and coupled on the receiving end. On the browser side, I began by implementing a basic audio streamer with an audio element. However, similar to video, there’s always a faster method possible. I used the AudioDecoder class from WebCodecs to implement a worklet class and jitterbuffer. In simple terms, this would decode audio on a separate thread, like WebCodecs prefers, and send the audio back to be played to the user. 31. Windows Video Encoding Speedups I made a few encoding speedups on Windows targets: The NVENC capture encode system uses a texture ring implementation: a pool of 3-5 encoder instances paired with a frame queue, allowing multiple frames to be encoded concurrently BGRA encoding is done directly in the library instead of using a third-party implementation Unchanged frames in DXGI capture are not encoded again, reducing CPU load 32. Mac Video Encoding Speedups I made a few encoding speedups on Mac targets: Request LatencyCritical activity for the session to ensure App Nap and other sleep measures don’t affect the thread Analogous to DXGI, skip encoding when CGDisplayStream reports no dirty rects Not an encoding speedup, but preferring 5 GHz for Mac hosted networks to ensure higher speeds Lotta stuff done this week, am I right? I made lots of progress and finished the bulk of my features, although Linux support is still in the works. I’ll make the 1.0.0 release soon and start a Stardance ship as well. I’ll likely add Linux support after 1.0.0, as I need to research EVDI and the WSL display compositor, as well as other minor tweaks for Windows/Mac. See y’all in the next devlog!"}, {"post_id": 54119, "ts": "2026-08-24T22:53:29Z", "hours": 12.6531, "text": "I speedran shipping a buncha features…in the span of a few days. I logged a copious amount of hours on Hackatime building random stuff. I tried some things with recording system audio and building a driver…those didn’t go the best, but I’ll cover that in the next devlog. Here are the main changes this week: 24. Even more quality of life changes Compared to last time, I wanted the user experience to improve even more. Specifically, for the Windows client, a custom display driver is required. On Macs, the functionality I need is built in, so none is required. However, uninstalling this display driver used to be a pain for users. Now, I bundled it as part of the uninstall script. I wasn’t sure how to do this, but Tauri has many useful guides and issues opened regarding custom WiX/NSIS code. When the window isn’t focused, join and leave notifications are toasted. This allows the user to stay informed even when the app isn’t open. Basic feature but very useful. I’d often get annoyed that I needed to keep re-entering the password to log in to a previously connected device. Hence, I added auto-join for devices based on their IP. This ensures that commonly connected devices can save time when reconnecting. 25. Onboarding screens The app is intuitive to use…or so I thought? Some of my beta testers were confused as to the various features and how to use the interface. Hence, I used NextStepjs to add onboarding components. This would dim the UI and only highlight the component being displayed, along with a message next to it. I liked the styles of NextStepjs a lot, so I chose it. There were some issues regarding window resize, so I made the onboarding exit and reopen to the last step when the window is resized. This way, the positioning of elements was forcefully recalculated. 26. Random security fixes My app had various vulnerabilities scattered throughout, so I ran a vulnerability Claude skill and patched some holes. The main issues fixed were: Checksums of bundled DLLs and resources are checked before being pushed to GitHub Actions Tauri CSP and allow list are actually enforced instead of being set to allow all TLS cert used for hosting the HTTPS domain has more secure file system permissions to reduce unwanted tampering of files Devices are trusted by a uniquely generated token, not by IP, to decrease the chance of impersonators 27. Command-line interface A majority of the time on this devlog was spent on the CLI. It wasn’t that hard (from a technical perspective), mainly just translating a lot of the UI code into Tauri CLI commands and mapping them correctly in the generated JSON file. One of the harder things to figure out was setting up cross-thread AppState, when the app wasn’t exactly running…turned out I just had to move the initialization command to before the window is created, so it would run inside both CLI and GUI modes. To present the QR codes better. I used qr2term to display the QR codes. By default, all logging is hidden. This turns ScreenExtend into a clean CLI mimicking the GUI. Hoorah! That’s it for this devlog. Not much to conclude, but plenty more features coming. Stay tuned!"}, {"post_id": 51902, "ts": "2026-08-21T20:12:46Z", "hours": 10.3639, "text": "Oh my lord…I crammed a lot of features into this week. I have yet to do proper audits of the codebase as a whole, specifically related to latency optimizations. I added a lot more code that affects the WebRTC pipeline, but I at least got the core features working. 20. Automatic updates For a while, the app relied on manual updating. It would check the latest version on Github, but users would have to go to the website, download the installer, uninstall the current version, and reinstall the newer version. This also messed up config and was a huge pain. Hence, I generated my own signing keys and used the Tauri updater plugin to automatically update apps. When the app is first launched and if an update is available, it automatically downloads and installs it without any user action. To make this more robust, I added a progress bar while downloading the update. On Windows specifically, the app must be shut down and restarted, so a blocking modal prevents devices from joining till this is done. I originally made this modal manually, but switched to using AlertDialog from ShadCN. Another issue I faced was the latest.json manifest. As I used custom filenames when downloading and installing updates, I had to manually patch the JSON. I learned a lot about the Tauri updater Github action through this, as I had to figure out how Tauri generated build filenames and how I could safely update them. 21. Quality of life for client devices I finished QOL fixes on the client side: Remember past devices to quickly import preferences or ban devices (all config saving is IP-based) Warn if the user is attempting to connect from the same device (this app is a remote control tool, but you can’t exactly remote control yourself) If the screen orientation changes, the virtual display is updated accordingly using CSS media queries to ensure a seamless experience A no fullscreen mode was added so users could quickly switch apps and maintain productivity 22. Quality of life for host devices I converted all in-app modals and error messages to JSON. This allowed me to add i18n language support, so I could easily translate my app to other languages. To detect and ensure only a single instance of the app was running, I previously used a file lock. However, there was an issue with MSI builds falsely detecting a file lock and not opening. I switched to a basic TCP model for IPC so that multiple processes running can figure out which was there first and shut down the others. 23. More more more Mac features! I’ve always felt a bit sad for my Mac users. I don’t have a Mac to test on, so I keep borrowing my dad’s Mac, which results in insanely slow development times. Nevertheless, I made some serious progress this week. The biggest thing was a simpler installation process. Many users would complain that they didn’t remember if they’re on Intel or Apple Silicon, so I now distribute a universal DMG. In terms of the app itself, I added a permissions check for screen recording and input injection. It would run a live test of screen capture or key input to see if the app actually had permissions; therefore, down the road it wouldn’t cause issues. I initially tried to do a soft check without an actual test (i.e., read the settings app), but this didn’t work. The only foolproof way was to call the API itself. The biggest feature by far was adding Mac input injection. Using accessibility APIs and a charmap similar to Windows, it would inject input onto the Mac screen. I could remotely log in from my Chromebook and get a lot of work done. I mirrored the structure and code on the Windows input injection pipeline, so no major difficulties here. That’s it for this week! Lotta stuff added, especially for the Mac users. I have a long list of features to continue building, specifically regarding easier Windows uninstall processes, onboarding, and an overall better UX. See you soon!"}, {"post_id": 39065, "ts": "2026-08-01T19:07:47Z", "hours": 9.8208, "text": "I had so much fun at the conference in San Francisco! I met a lot of cool people who gave me advice on the app, although I didn’t see the downloads spike 😭. I primarily began working on a SW encoder and other miscellaneous changes: 17. Remote control toggle Sometimes, the user doesn’t want a client to have remote control access. Although the purpose of the app is to have remote control, it is useful to disable this feature. On the frontend, I added a toggle switch that enables or disables the remote control backend. I was debating whether WebRTC negotiation was required as well, but decided to do so in the end. The remote control used data channels, so an unused data channel would result in slightly lower latency. Renegotiation ensured the stream stayed as efficient as possible without sending redundant data. 18. Update checking In order for current users to access newer versions, I wanted to add an update functionality. I’m not using Tauri’s update plugin just yet, so I run a simple check. The app, on startup, checks the latest release tag on GitHub. If it’s not equal to the current version (rollback or higher version), the user is prompted to download the newer version. Automatic updates will come next. 19. The big boy: software encoding This one’s been a long time coming. A lot of machines like VMs or computers with a weird setup just don’t have a working hardware encoder (Intel/AMD/Nvidia), and until now that meant the app straight up didn’t work there. So I built a full CPU-only x264 fallback. For the pipeline, the frames come as raw BGRA bytes that the other supporters already encoded. They must be converted to I420 before going to libx264, whose build I pulled from OBS. As a side note, the DLL is bundled with the app at runtime to ensure no missing dependencies. As with any encoder, the rate control is ABR with a tight VBV, as well as an infinite GOP. Besides the technical jargon, this just means latency is the north star and nothing else matters. The whole software variant is added to the encoder vendor enum, and wired into the fallback chain: if NVENC fails, try Intel; if Intel fails, fall back to software before giving up entirely. There’s also a manual override if someone really prefers software encoding, but this is not the default. There’s a big CPU cost compared to hardware encoding, so it’s very much a last resort and only kicks in if absolutely no GPU encoders are available. But “last resort that works” beats “doesn’t work at all.” That’s it for this week! I plan to have some smaller quality-of-life changes and better updates coming soon…"}, {"post_id": 34237, "ts": "2026-07-24T19:29:18Z", "hours": 9.1139, "text": "Sooooo this devlog is mostly comprised of website changes. I have a conference in San Francisco and wanted to polish up the app before publishing. Here are the main changes I made: 13. Random chores and updating the website I absolutely love shadcn-ui, but so do AI models. In past reviews by stardance raters, as well as family and friends, they pointed out the website looked AI-generated. Hence, I decreased the number of gradients and added basic animations to make the site feel more alive. This was a first for me to implement animations, but the Tailwind docs made it quite simple. I used a specific code-like font instead of the generic fonts I had and tidied up CSS classes. The biggest change was adding a blurred-geometric background for a nicer feel. 14. Redesigning the website completely I felt it was better to update the website as a whole, so I changed up the user interface. When I tested the app with real users, they complained that they didn’t know how to actually use the app. I changed the hero design to have clearer text and added an SVG-animation component showing how the site actually works. The feature section stayed the same, but there was a highlights grid (inspo https://cider.sh/ ) to highlight the main features. The FAQ was shortened to my liking, and I pulled some utilities from Tailwind for animations. In terms of images, I updated the images of the app featured on the website and cleaned up some of the dimensions related to that CSS. The old homepage still lives inside OldHome() but isn’t used anymore. 15. Tidying up the README and build workflows I edited the README to remove redundant information and make it more developer-focused. I also updated some information regarding GPU builds. For the build workflow, I ensured it would only run if the commit ended in “rebuild”. This ensures unnecessary build minutes are not spent on small changes not requiring a full rebuild. 16. Wake lock API fallback This app aims to support all devices, but older devices have…funny APIs to say the least. For WebRTC, I implemented the legacy encoded streams API. However, the device would still turn off for seemingly no reason while the video was playing. On older devices, the wake lock API is not supported. However, there’s a unique workaround for this: any video element playing on the webpage will keep the screen from dimming. Hence, I used the NoSleep library to create an off-screen 1-second video element that keeps looping, ensuring the display doesn’t go dark. Some very fun stuff with browsers. That’s it for this devlog! Mostly minor changes, but the next one will have better SW encoding support among other changes. Stay tuned…"}, {"post_id": 28772, "ts": "2026-07-15T08:02:33Z", "hours": 10.7358, "text": "I met with some friends to discuss my app…and realized I gotta lock in with the features. Among the various topics we discussed like the difficult UI or lack of automatic updates, they really wanted remote control features. So, I set out to make the remote display something I can actually touch, click, and type into. And patching some other bugs along the way. 9. Remote input This is the main headline feature. Streaming pixels one way was solved; now the client had to send input back up the wire and have the host replay it as if it came from real hardware. Host side (Windows). A new input backend injects events through a synthetic pointer device , so mouse, touch, and pen all seem real as opposed to faked mouse moves. The synthetic device can execute touch or mouse movements from the tablet client. macOS and Linux backends are stubbed and will come later (trust me bro). The wire. This side had to be optimized for dead-low latency like the rest of the project. Instead of passing strings or using some wrapper library, I made my own binary protocol (a dedicated protocol module) serialized over WebRTC data channels , not the media path. Client-side input.js captures keyboard, mouse, pointer (touch/pen), wheel, clipboard, and drag-drop and ships them across. The code basically catches every single frontend input event and forwards all those events (20+)! The routing is latency-first, same as everything else, with three different paths. fast-path events (pointer moves, wheel) take an unreliable channel , where a dropped packet is cheaper than a late one, while clicks, keys, and clipboard take the reliable channel where every event must land exactly once. It’s not that big of a deal if one small mouse delta doesn’t go through, but it definitely is a big deal if a click doesn’t go through. 10. Browser compatibility detections Not every browser can do WebCodecs, secure contexts, or the input paths above. Rather than fail silently, I added a compatibility check on both the browser and client up front. It shows compatibility modals with per-feature lists to ensure the user knows what’s going on. 11. Some more small fixes I added two quality-of-life additions: avatars (upload custom profile pictures) and zoom controls (ctrl/cmd +/-). 12. Encoders that don’t explode when funny stuff happens Oh my god…one of the biggest issues by far. When I ran the NVIDIA encoder at high FPS, it didn’t crash. However, the Intel Quick Sync encoder would arbitrarily crash at high FPS and wouldn’t give a reason why. I audited a buncha different parts of my code and eventually found that FPS was the issue, not resolution or low-latency flags. Hence, I added a binary search that probes the hardware for its true maximum supported fps and clamps to it before the encoder can choke. Essentially, I ask the encoder “can you do 1920x1080 at 240 fps?” –> “no” –> “what about 120 fps?” –> “yes” –> “what about 180 fps?”. This keeps repeating till the max FPS is found and streamed to the client. Rounding out the release, I made a variety of different UI fixes like the modal fixes , backdrop overlap, a stray uninstall-modal backdrop, and cloud reconfig errors . More progress to come!"}, {"post_id": 20389, "ts": "2026-07-01T23:35:58Z", "hours": 54.7942, "text": "ScreenExtend Devlog: Building the Rust Engine Under a Wireless Second Monitor TL;DR ScreenExtend turns any device with a browser into a real, wireless extended monitor for a Windows host, no client app and no cables (it began so my brother could use his iPad for notes at debate tournaments). Scan a QR code and the host spins up a real virtual display, streamed over WebRTC with hardware H.264. The React/TS control UI existed before; everything else (Rust core, GPU pipeline, WebCodecs client, cloud relay) was built from scratch , always latency-first, quality-second. 1. The spine: Tauri + a typed Rust↔TS bridge tauri-specta exports a fully-typed src/lib/bindings.ts for commands (TS→Rust) plus typed events (Rust→TS): devices, network, cloud status, logs. A code-generated bridge beats maintaining one by hand. Let the code write itself! 2. Virtual displays: faking a real monitor Each client gets its own virtual display the OS treats as real hardware, a true second screen, not mirroring. On join the host creates one sized to the client , forces extend topology, then captures it once attached. On Windows a signed IDD driver installs at runtime (via nefconc + certutil ), and it tears down cleanly on leave. 3. First light: WebRTC + WHEP + H.264 The media path is WebRTC (real-time, in every browser). Signaling is WHEP : the client POSTs an SDP offer, the host answers in-response, no signaling server. Codec is H.264 , universal hardware decode on clients and encode (NVENC/QSV/AMF) on hosts. The first release streamed NVENC to a plain <video> ; it worked but was laggy. It binds HTTP + HTTPS (self-signed via rcgen ) , since WebCodecs needs a secure context. 4. The pipeline: capture → encode → broadcast Capture is Windows Graphics Capture (later a custom fork; DXGI Desktop Duplication fallback), with a repeater thread for idle keepalive/IDRs and transient-stall tolerance. 5. Going vendor-specific: the part that made it fast Generic capture→CPU-copy→encode is slow everywhere; the win: keep the frame on the GPU from capture to bitstream. Per vendor: NVENC (NVIDIA): no SDK linked (API dynamically loaded ); zero-copy writes capture into a shared D3D11 texture behind a keyed mutex, else a CPU bridge. Intel QSV/oneVPL: same-adapter capture+encode fuses the downscale into the VPP pass (BGRA→NV12 + resize in one shot). macOS (VideoToolbox): ScreenCaptureKit (12.3+, CGDisplayStream fallback) via objc2 runtime interop only , so one binary loads on every macOS version, same IOSurface zero-copy. 6. The client: WebCodecs over <video> Fast path: an RTCRtpScriptTransform feeds a worker VideoDecoder ( optimizeForLatency: true ) onto an OffscreenCanvas , latency hints zeroed. A <video> fallback covers missing WebCodecs or stalls. It also feels like a monitor: fullscreen + input/wake locks, /leave beacon on exit. 7. Adaptive bitrate, sessions & security A BWE driver reads getStats every 250ms, pushing a smoothed bitrate into the encoder. Changing settings triggers in-place renegotiation (a reconfig/kick epoch the client polls) with no display teardown. Each host is gated by a session ID + 6-digit OTP ( OtpLimiter lockout); every device is isolated, over HTTPS + DTLS/SRTP. 8. Networking + status (v0.2.3) Same-network is direct, no servers. Offline mode stands up an ad-hoc hosted network , no infrastructure needed. Cross-network adds a cloud relay (WebSocket to session.screenextend.app ) tunneling signaling while media stays P2P; a self-hosted TURN server covers hard NATs. A logbus feeds an in-app terminal. Working today: Windows/macOS hosts, per-vendor GPU encode, per-device displays, adaptive bitrate, OTP sessions, offline mode, and cloud relay."}]...
|
Title: ScreenExtend by @epiccodewizard | Stardance Title: ScreenExtend by @epiccodewizard | Stardance
Devlogs:
ScreenExtend Devlog: Building the Rust Engine Under a Wireless Second Monitor TL;DR ScreenExtend turns any device with a browser into a real, wireless extended monitor for a Windows host, no client app and no cables (it began so my brother could use his iPad for notes at debate tournaments). Scan a QR code and the host spins up a real virtual display, streamed over WebRTC with hardware H.264. The React/TS control UI existed before; everything else (Rust core, GPU pipeline, WebCodecs client, cloud relay) was built from scratch , always latency-first, quality-second. 1. The spine: Tauri + a typed Rust↔TS bridge tauri-specta exports a fully-typed src/lib/bindings.ts for commands (TS→Rust) plus typed events (Rust→TS): devices, network, cloud status, logs. A code-generated bridge beats maintaining one by hand. Let the code write itself! 2. Virtual displays: faking a real monitor Each client gets its own virtual display the OS treats as real hardware, a true second screen, not mirroring. On join the host creates one sized to the client , forces extend topology, then captures it once attached. On Windows a signed IDD driver installs at runtime (via nefconc + certutil ), and it tears down cleanly on leave. 3. First light: WebRTC + WHEP + H.264 The media path is WebRTC (real-time, in every browser). Signaling is WHEP : the client POSTs an SDP offer, the host answers in-response, no signaling server. Codec is H.264 , universal hardware decode on clients and encode (NVENC/QSV/AMF) on hosts. The first release streamed NVENC to a plain <video> ; it worked but was laggy. It binds HTTP + HTTPS (self-signed via rcgen ) , since WebCodecs needs a secure context. 4. The pipeline: capture → encode → broadcast Capture is Windows Graphics Capture (later a custom fork; DXGI Desktop Duplication fallback), with a repeater thread for idle keepalive/IDRs and transient-stall tolerance. 5. Going vendor-specific: the part that made it fast Generic capture→CPU-copy→encode is slow everywhere; the win: keep the frame on the GPU from capture to bitstream. Per vendor: NVENC (NVIDIA): no SDK linked (API dynamically loaded ); zero-copy writes capture into a shared D3D11 texture behind a keyed mutex, else a CPU bridge. Intel QSV/oneVPL: same-adapter capture+encode fuses the downscale into the VPP pass (BGRA→NV12 + resize in one shot). macOS (VideoToolbox): ScreenCaptureKit (12.3+, CGDisplayStream fallback) via objc2 runtime interop only , so one binary loads on every macOS version, same IOSurface zero-copy. 6. The client: WebCodecs over <video> Fast path: an RTCRtpScriptTransform feeds a worker VideoDecoder ( optimizeForLatency: true ) onto an OffscreenCanvas , latency hints zeroed. A <video> fallback covers missing WebCodecs or stalls. It also feels like a monitor: fullscreen + input/wake locks, /leave beacon on exit. 7. Adaptive bitrate, sessions & security A BWE driver reads getStats every 250ms, pushing a smoothed bitrate into the encoder. Changing settings triggers in-place renegotiation (a reconfig/kick epoch the client polls) with no display teardown. Each host is gated by a session ID + 6-digit OTP ( OtpLimiter lockout); every device is isolated, over HTTPS + DTLS/SRTP. 8. Networking + status (v0.2.3) Same-network is direct, no servers. Offline mode stands up an ad-hoc hosted network , no infrastructure needed. Cross-network adds a cloud relay (WebSocket to session.screenextend.app ) tunneling signaling while media stays P2P; a self-hosted TURN server covers hard NATs. A logbus feeds an in-app terminal. Working today: Windows/macOS hosts, per-vendor GPU encode, per-device displays, adaptive bitrate, OTP sessions, offline mode, and cloud relay.
I met with some friends to discuss my app…and realized I gotta lock in with the features. Among the various topics we discussed like the difficult UI or lack of automatic updates, they really wanted remote control features. So, I set out to make the remote display something I can actually touch, click, and type into. And patching some other bugs along the way. 9. Remote input This is the main headline feature. Streaming pixels one way was solved; now the client had to send input back up the wire and have the host replay it as if it came from real hardware. Host side (Windows). A new input backend injects events through a synthetic pointer device , so mouse, touch, and pen all seem real as opposed to faked mouse moves. The synthetic device can execute touch or mouse movements from the tablet client. macOS and Linux backends are stubbed and will come later (trust me bro). The wire. This side had to be optimized for dead-low latency like the rest of the project. Instead of passing strings or using some wrapper library, I made my own binary protocol (a dedicated protocol module) serialized over WebRTC data channels , not the media path. Client-side input.js captures keyboard, mouse, pointer (touch/pen), wheel, clipboard, and drag-drop and ships them across. The code basically catches every single frontend input event and forwards all those events (20+)! The routing is latency-first, same as everything else, with three different paths. fast-path events (pointer moves, wheel) take an unreliable channel , where a dropped packet is cheaper than a late one, while clicks, keys, and clipboard take the reliable channel where every event must land exactly once. It’s not that big of a deal if one small mouse delta doesn’t go through, but it definitely is a big deal if a click doesn’t go through. 10. Browser compatibility detections Not every browser can do WebCodecs, secure contexts, or the input paths above. Rather than fail silently, I added a compatibility check on both the browser and client up front. It shows compatibility modals with per-feature lists to ensure the user knows what’s going on. 11. Some more small fixes I added two quality-of-life additions: avatars (upload custom profile pictures) and zoom controls (ctrl/cmd +/-). 12. Encoders that don’t explode when funny stuff happens Oh my god…one of the biggest issues by far. When I ran the NVIDIA encoder at high FPS, it didn’t crash. However, the Intel Quick Sync encoder would arbitrarily crash at high FPS and wouldn’t give a reason why. I audited a buncha different parts of my code and eventually found that FPS was the issue, not resolution or low-latency flags. Hence, I added a binary search that probes the hardware for its true maximum supported fps and clamps to it before the encoder can choke. Essentially, I ask the encoder “can you do 1920x1080 at 240 fps?” –> “no” –> “what about 120 fps?” –> “yes” –> “what about 180 fps?”. This keeps repeating till the max FPS is found and streamed to the client. Rounding out the release, I made a variety of different UI fixes like the modal fixes , backdrop overlap, a stray uninstall-modal backdrop, and cloud reconfig errors . More progress to come!
Sooooo this devlog is mostly comprised of website changes. I have a conference in San Francisco and wanted to polish up the app before publishing. Here are the main changes I made: 13. Random chores and updating the website I absolutely love shadcn-ui, but so do AI models. In past reviews by stardance raters, as well as family and friends, they pointed out the website looked AI-generated. Hence, I decreased the number of gradients and added basic animations to make the site feel more alive. This was a first for me to implement animations, but the Tailwind docs made it quite simple. I used a specific code-like font instead of the generic fonts I had and tidied up CSS classes. The biggest change was adding a blurred-geometric background for a nicer feel. 14. Redesigning the website completely I felt it was better to update the website as a whole, so I changed up the user interface. When I tested the app with real users, they complained that they didn’t know how to actually use the app. I changed the hero design to have clearer text and added an SVG-animation component showing how the site actually works. The feature section stayed the same, but there was a highlights grid (inspo https://cider.sh/ ) to highlight the main features. The FAQ was shortened to my liking, and I pulled some utilities from Tailwind for animations. In terms of images, I updated the images of the app featured on the website and cleaned up some of the dimensions related to that CSS. The old homepage still lives inside OldHome() but isn’t used anymore. 15. Tidying up the README and build workflows I edited the README to remove redundant information and make it more developer-focused. I also updated some information regarding GPU builds. For the build workflow, I ensured it would only run if the commit ended in “rebuild”. This ensures unnecessary build minutes are not spent on small changes not requiring a full rebuild. 16. Wake lock API fallback This app aims to support all devices, but older devices have…funny APIs to say the least. For WebRTC, I implemented the legacy encoded streams API. However, the device would still turn off for seemingly no reason while the video was playing. On older devices, the wake lock API is not supported. However, there’s a unique workaround for this: any video element playing on the webpage will keep the screen from dimming. Hence, I used the NoSleep library to create an off-screen 1-second video element that keeps looping, ensuring the display doesn’t go dark. Some very fun stuff with browsers. That’s it for this devlog! Mostly minor changes, but the next one will have better SW encoding support among other changes. Stay tuned…
I had so much fun at the conference in San Francisco! I met a lot of cool people who gave me advice on the app, although I didn’t see the downloads spike 😭. I primarily began working on a SW encoder and other miscellaneous changes: 17. Remote control toggle Sometimes, the user doesn’t want a client to have remote control access. Although the purpose of the app is to have remote control, it is useful to disable this feature. On the frontend, I added a toggle switch that enables or disables the remote control backend. I was debating whether WebRTC negotiation was required as well, but decided to do so in the end. The remote control used data channels, so an unused data channel would result in slightly lower latency. Renegotiation ensured the stream stayed as efficient as possible without sending redundant data. 18. Update checking In order for current users to access newer versions, I wanted to add an update functionality. I’m not using Tauri’s update plugin just yet, so I run a simple check. The app, on startup, checks the latest release tag on GitHub. If it’s not equal to the current version (rollback or higher version), the user is prompted to download the newer version. Automatic updates will come next. 19. The big boy: software encoding This one’s been a long time coming. A lot of machines like VMs or computers with a weird setup just don’t have a working hardware encoder (Intel/AMD/Nvidia), and until now that meant the app straight up didn’t work there. So I built a full CPU-only x264 fallback. For the pipeline, the frames come as raw BGRA bytes that the other supporters already encoded. They must be converted to I420 before going to libx264, whose build I pulled from OBS. As a side note, the DLL is bundled with the app at runtime to ensure no missing dependencies. As with any encoder, the rate control is ABR with a tight VBV, as well as an infinite GOP. Besides the technical jargon, this just means latency is the north star and nothing else matters. The whole software variant is added to the encoder vendor enum, and wired into the fallback chain: if NVENC fails, try Intel; if Intel fails, fall back to software before giving up entirely. There’s also a manual override if someone really prefers software encoding, but this is not the default. There’s a big CPU cost compared to hardware encoding, so it’s very much a last resort and only kicks in if absolutely no GPU encoders are available. But “last resort that works” beats “doesn’t work at all.” That’s it for this week! I plan to have some smaller quality-of-life changes and better updates coming soon…
Oh my lord…I crammed a lot of features into this week. I have yet to do proper audits of the codebase as a whole, specifically related to latency optimizations. I added a lot more code that affects the WebRTC pipeline, but I at least got the core features working. 20. Automatic updates For a while, the app relied on manual updating. It would check the latest version on Github, but users would have to go to the website, download the installer, uninstall the current version, and reinstall the newer version. This also messed up config and was a huge pain. Hence, I generated my own signing keys and used the Tauri updater plugin to automatically update apps. When the app is first launched and if an update is available, it automatically downloads and installs it without any user action. To make this more robust, I added a progress bar while downloading the update. On Windows specifically, the app must be shut down and restarted, so a blocking modal prevents devices from joining till this is done. I originally made this modal manually, but switched to using AlertDialog from ShadCN. Another issue I faced was the latest.json manifest. As I used custom filenames when downloading and installing updates, I had to manually patch the JSON. I learned a lot about the Tauri updater Github action through this, as I had to figure out how Tauri generated build filenames and how I could safely update them. 21. Quality of life for client devices I finished QOL fixes on the client side: Remember past devices to quickly import preferences or ban devices (all config saving is IP-based) Warn if the user is attempting to connect from the same device (this app is a remote control tool, but you can’t exactly remote control yourself) If the screen orientation changes, the virtual display is updated accordingly using CSS media queries to ensure a seamless experience A no fullscreen mode was added so users could quickly switch apps and maintain productivity 22. Quality of life for host devices I converted all in-app modals and error messages to JSON. This allowed me to add i18n language support, so I could easily translate my app to other languages. To detect and ensure only a single instance of the app was running, I previously used a file lock. However, there was an issue with MSI builds falsely detecting a file lock and not opening. I switched to a basic TCP model for IPC so that multiple processes running can figure out which was there first and shut down the others. 23. More more more Mac features! I’ve always felt a bit sad for my Mac users. I don’t have a Mac to test on, so I keep borrowing my dad’s Mac, which results in insanely slow development times. Nevertheless, I made some serious progress this week. The biggest thing was a simpler installation process. Many users would complain that they didn’t remember if they’re on Intel or Apple Silicon, so I now distribute a universal DMG. In terms of the app itself, I added a permissions check for screen recording and input injection. It would run a live test of screen capture or key input to see if the app actually had permissions; therefore, down the road it wouldn’t cause issues. I initially tried to do a soft check without an actual test (i.e., read the settings app), but this didn’t work. The only foolproof way was to call the API itself. The biggest feature by far was adding Mac input injection. Using accessibility APIs and a charmap similar to Windows, it would inject input onto the Mac screen. I could remotely log in from my Chromebook and get a lot of work done. I mirrored the structure and code on the Windows input injection pipeline, so no major difficulties here. That’s it for this week! Lotta stuff added, especially for the Mac users. I have a long list of features to continue building, specifically regarding easier Windows uninstall processes, onboarding, and an overall better UX. See you soon!
I speedran shipping a buncha features…in the span of a few days. I logged a copious amount of hours on Hackatime building random stuff. I tried some things with recording system audio and building a driver…those didn’t go the best, but I’ll cover that in the next devlog. Here are the main changes this week: 24. Even more quality of life changes Compared to last time, I wanted the user experience to improve even more. Specifically, for the Windows client, a custom display driver is required. On Macs, the functionality I need is built in, so none is required. However, uninstalling this display driver used to be a pain for users. Now, I bundled it as part of the uninstall script. I wasn’t sure how to do this, but Tauri has many useful guides and issues opened regarding custom WiX/NSIS code. When the window isn’t focused, join and leave notifications are toasted. This allows the user to stay informed even when the app isn’t open. Basic feature but very useful. I’d often get annoyed that I needed to keep re-entering the password to log in to a previously connected device. Hence, I added auto-join for devices based on their IP. This ensures that commonly connected devices can save time when reconnecting. 25. Onboarding screens The app is intuitive to use…or so I thought? Some of my beta testers were confused as to the various features and how to use the interface. Hence, I used NextStepjs to add onboarding components. This would dim the UI and only highlight the component being displayed, along with a message next to it. I liked the styles of NextStepjs a lot, so I chose it. There were some issues regarding window resize, so I made the onboarding exit and reopen to the last step when the window is resized. This way, the positioning of elements was forcefully recalculated. 26. Random security fixes My app had various vulnerabilities scattered throughout, so I ran a vulnerability Claude skill and patched some holes. The main issues fixed were: Checksums of bundled DLLs and resources are checked before being pushed to GitHub Actions Tauri CSP and allow list are actually enforced instead of being set to allow all TLS cert used for hosting the HTTPS domain has more secure file system permissions to reduce unwanted tampering of files Devices are trusted by a uniquely generated token, not by IP, to decrease the chance of impersonators 27. Command-line interface A majority of the time on this devlog was spent on the CLI. It wasn’t that hard (from a technical perspective), mainly just translating a lot of the UI code into Tauri CLI commands and mapping them correctly in the generated JSON file. One of the harder things to figure out was setting up cross-thread AppState, when the app wasn’t exactly running…turned out I just had to move the initialization command to before the window is created, so it would run inside both CLI and GUI modes. To present the QR codes better. I used qr2term to display the QR codes. By default, all logging is hidden. This turns ScreenExtend into a clean CLI mimicking the GUI. Hoorah! That’s it for this devlog. Not much to conclude, but plenty more features coming. Stay tuned!
Oh my lord, I keep forgetting to write devlogs every 10 hours and am losing hours on each one. Nevertheless, I finally finished audio work! Plus some other things like running claude security agents on the code base and patching stupid vulnerabilities. Here’s how the week went: 28. HiDPI and Mac Issues Whenever I use my Mac with ScreenExtend, the stream looks blurry due to Macs having HiDPI screens. I fixed this by adding basic HiDPI scaling on Windows. The UI has a DPR slider based on autodetected values. This DPR is multiplied by the screen resolution for a shaper display. One issue that arose with the new displays was the fact that the display would randomly not scale properly, so the task bar and apps would appear tiny. To mitigate this, I reapplied all display settings after resolution changes and a timeout. 29. AUDIO WORKS!!!! For the longest time, I tried getting audio to work. On Windows, the process was straightforward: a WASAPI loopback system audio capture session. I wanted to build my own custom driver to capture audio faster, but this would be too expensive to deploy. I read other Rust implementations and quickly wrote the code. On the other hand, Mac was a whole different issue. On older versions of Mac, system audio recording is not supported. Hence, for Macs below 13.0, I wrote a custom audio driver. I didn’t have a clue about implementing an AudioServerPlugIn on Mac, but worked out the specifics by looking at the Blackhole audio driver. I did have to proxy physical volume buttons to work (i.e. I listen for taps and change the audio level within the driver). For older Macs, I used ScreenCaptureKit with an extra audio flag. This was a simple fix to capture audio. On even newer Macs, there’s a Process Tap API to record system audio with low latency. Once again, I didn’t have much difficulties implementing these due to the abundance of documentation. 30. Streaming Audio over WebRTC (with A/V sync) When sending video and audio separately, the two are prone to fall out of sync, so watching movies or videos is difficult. To mitigate these issues, I wrote an A/V sync host clock. This packages the time on the host clock to audio samples and video frames, such that they can be reconstructed and coupled on the receiving end. On the browser side, I began by implementing a basic audio streamer with an audio element. However, similar to video, there’s always a faster method possible. I used the AudioDecoder class from WebCodecs to implement a worklet class and jitterbuffer. In simple terms, this would decode audio on a separate thread, like WebCodecs prefers, and send the audio back to be played to the user. 31. Windows Video Encoding Speedups I made a few encoding speedups on Windows targets: The NVENC capture encode system uses a texture ring implementation: a pool of 3-5 encoder instances paired with a frame queue, allowing multiple frames to be encoded concurrently BGRA encoding is done directly in the library instead of using a third-party implementation Unchanged frames in DXGI capture are not encoded again, reducing CPU load 32. Mac Video Encoding Speedups I made a few encoding speedups on Mac targets: Request LatencyCritical activity for the session to ensure App Nap and other sleep measures don’t affect the thread Analogous to DXGI, skip encoding when CGDisplayStream reports no dirty rects Not an encoding speedup, but preferring 5 GHz for Mac hosted networks to ensure higher speeds Lotta stuff done this week, am I right? I made lots of progress and finished the bulk of my features, although Linux support is still in the works. I’ll make the 1.0.0 release soon and start a Stardance ship as well. I’ll likely add Linux support after 1.0.0, as I need to research EVDI and the WSL display compositor, as well as other minor tweaks for Windows/Mac. See y’all in the next devlog!
One step closer to finally shipping…or so I hope. I speedran shipping this next feature, so it took a lot of time. This feature is mainly for myself. I have an old Mac (running 10.13 from 2017, really old I get itttt). I want my app to run on there, but it currently only supports 10.15+. Hence, I went down a funny rabbit hole in an attempt to add support. 33. Cleaning up dylib signature lookups at run time One of the key challenges I faced throughout development was how to have a unified codebase for multiple API versions. I decided that instead of version-gating APIs and guessing which beta/normal versions they would be accessible on, I’d test for APIs at run time. I changed VideoToolbox to use this logic: it would attempt to set/use APIs and variables available at higher runtimes, but would work fine without them and have fallbacks. This ensured both legacy and modern systems functioned well. 34. Airplay automations…? As I was doing competitor research, I noticed many of the other apps supported Mac 10.13-10.15 as well as 10.15+, while mainly only supported 10.15+. Essentially, they started a fake Airplay server, had the Mac connect it, and set the display mode to “Use as separate display”. This would create a virtual display that the app would then use for its own functions. As ScreenExtend requires its own display on older machines, similar to this, where CGVirtualDisplay isn’t an option, I decided to use this method. I had my app automate opening the Airplay menu and connecting as well, in addition to running the fake server. Most of the process was straightforward, especially due to the abundance of fake Airplay server implementations online and my own experience with apps like LonelyScreen. The one issue of this approach is that only one display can be connected at a time, so any tertiary devices wanting to connect have to wait their turn. I guess the only option is to by a new mac then… 35. Client UI update cleanliness fixes Due to the one device restriction, I implemented a separate page that would be served if the display slot was being used up. It would be coupled with a short message explaining the consequences of the older Mac version. There were also bugs related to session teardown (keyboard still being locked, video stream still active in the background, etc) that I quickly squashed after finding them. Soooo in hindsight, I prolly should’ve bumped this up to 0.6.0, but I left it at 0.5.4. Oh well, I’ll increment the version by next release. Only a few more features to go on the feature list before hitting v1 and shipping!!!
Ships:
Initial release for ScreenExtend! Please give feedback and read the initial devlog (shorter one here, longer one at https://github.com/ScreenExtend/ScreenExtend/blob/main/DEVLOG.md ) for detailed information....
|
1792842964.262038
|
1792929364.262038
|
|
26398
|
200 MPH Drone RTOS by @Guagyyy | Stardance
|
Guagyyy
|
2
|
129.28
|
2267
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-22T00:49:05Z", "hours": 106.0, "multiplier": 19.18, "payout": 2024, "blessing": "neutral", "text": "200 MPH Drone RTOS: How it went Simulation to Silicon! When I published the first major update for this project at the 15-hour mark, I essentially had a proof-of-concept. I had managed to write a custom hybrid cooperative/preemptive scheduler and get an ARM Cortex-M7 (on the STM32f756VGTx) assembly context switcher functioning inside the Renode simulator. Although this looked like a massive leap, the project had barely started. Over 100 hours of development have gone into this project since then. The transition from a simulated kernel to a bare-metal flight controller capable of keeping a 200 MPH drone in the air required stripping the system down to its bare components, rewriting the memory architecture, and debugging some of the most frustrating hardware faults I have ever encountered-especially whenever the project just crashed, leaving me no traces as to what went wrong. Software Limitations The biggest turning point was realizing that software simulation can only take you so far. Renode is an amazing tool, but it hid a massive amount of race conditions and timing flaws that only exposed themselves when I finally flashed the code to physical STM32F756 Nucleo board. Sometimes, the MCU would have to reboot every 2 seconds. I spent dozens of hours tracking down massive architectural bugs. One of the worst was a context-switching fault where floating-point math was corrupting the return address in the CPU registers, causing the processor to execute raw garbage data. To fix this, I had to completely restructure the boot sequence and overhaul the memory management. I moved away from static arrays to a dynamic per-task stack allocation system, and implemented a dual-guard memory system. Arguably, this was the most important change as it allowed me to debug tasks way quicker as I instantly could know when the canary values were corrupted. Overhauling the Data Pipelines In order to avoid the 10ms delay of my original blocking pipline (for data transfer), I entirely rewrote the hardware data pipelines multiple times to use a semi-blocking, circular Direct Memory Access (DMA) architecture. Instead of the processor handling the data transfer, the hardware itself moves the bytes from the sensors directly into memory in the background. The DMA streams now trigger precise interrupts at the halfway and full-transfer points, allowing the RTOS to safely parse incoming radio packets and sensor reads without ever risking data overwrites or blocking the main flight loop. Building a Flight Controller I integrated Betaflight’s Actual Rates algorithm with custom expo curves, ensuring that physical stick inputs map perfectly to target velocities. I built a custom SPI driver to pull 24-bit raw pressure and temperature data from a BMP390 barometer, passing it through hypsometric formulas to calculate altitude. For video and telemetry, I moved away from raw UART strings and implemented native MSP DisplayPort communication (for true digital Walksnail Avatar connection). I also built an interactive USB command-line interface. Instead of having to recompile and re-flash the firmware every time a PID value needs to be adjusted, I can plug into the board, pull up a terminal, and manually tune gains, remap CRSF radio channels, or adjust IMU orientation offsets in real time. Final Version The last few weeks have been about refinement and consistency, removing race conditions and overall polishing the code. Now that the RTOS is fully functional, the only things to add are extra features, like a permanent + more expanded CLI, as well as finishing the external RM and linking the ESC drivers. As these are pretty insignificant, I have linked the source of this shipment to the Github project rather than a specific release so that I can update these during review!", "repo": "https://github.com/Guag914/200-MPH-Drone-RTOS/", "demo": "https://github.com/Guag914/200-MPH-Drone-RTOS#interactive--simulation-demo"}, {"ship_num": 2, "ts": "2026-07-15T04:02:12Z", "hours": 15.0, "multiplier": 16.44, "payout": 243, "blessing": "neutral", "text": "Phase 1 (The Custom RTOS Kernel) I am building a custom FPV drone from scratch with my friend in an attempt to achieve 200 mph . At those speeds, standard super loops are far too slow, as a single microsecond of lag means the drone travels feet before making a PID correction. To solve this, I built a custom, hybrid cooperative/preemptive Real-Time Operating System (RTOS) from scratch for the ARM Cortex-M7 (STM32F7)! What Was Challenging? The absolute hardest part of this phase was writing the low-level assembly context switcher ( PendSV_Handler.S ). Hacking the ARM Cortex-M7 registers to physically hot-swap stack pointers ( PSP ) mid-execution without crashing the CPU or corrupting the program state was a massive headache. I had to ensure that when an asynchronous preemption event fires, the assembly code perfectly preserves the execution registers ( R4-R11 plus the hardware-stacked registers), jumps to the high-priority task, and then seamlessly restores the background task right where it left off. Getting the compiler-enforced task overloading working so the kernel cleanly separates periodic tasks from raw interrupts was another huge hurdle, but it makes the whole codebase infinitely more robust. Want to Test It or Write Your Own Tasks? You don’t need physical hardware to watch this run! I’ve packaged the entire project with a Renode simulation script. You can run the interactive emulator with a single command to see the preemption scheduler hijack our counter task live, or you can jump into the user sandbox file and write your own custom tasks. Check out the GitHub Repository to Run the Demo! The README has a quick 5-minute setup guide to get you compiling and simulating your own code in no time.", "repo": "https://github.com/Guag914/200-MPH-Drone-RTOS/", "demo": "https://github.com/Guag914/200-MPH-Drone-RTOS#interactive--simulation-demo"}]...
|
[{"post_id": 78931, "ts": "202 [{"post_id": 78931, "ts": "2026-10-12T02:34:08Z", "hours": 8.2783, "text": "Although I haven’t been able to make much progress on the project due to school over the past weeks I did manage to make some substantial project mostly with the scheduler, allowing for fairly good performance on my STM Nucleo boards, rather than it just crashing instantly. 1. Issues with the Old Design The flight controller’s RTOS scheduler wasn’t working under real hardware timing because global variables like currTaskIndex, topOfStack, and excReturn were being modified from multiple entry points simultaneously. Yield calls, interrupt handlers, and the main task-selection logic were all making independent writes to the same shared task control blocks. Whenever a hardware interrupt fired mid-write, stack pointers and exception return values were saved into the wrong taskControlBlocks slot. This created edge cases where preempted tasks were silently double-queued, state tracking completely desynchronized, and time-critical hardware events were dropped during flight. 2. Centralizing Context Switches in PendSV To eliminate these race conditions, all state-selection decisions were moved out of individual execution contexts and consolidated into decideNextTask(), which now runs exclusively inside the PendSV assembly handler. Functions like yieldCurrentTask(), decideNextInterruptTask(), and decideNextScheduledTask() no longer touch global scheduler state or choose which task runs next; they simply update local status flags like TaskState::BLOCKED or TaskState::READY inside brief __disable_irq() critical sections and trigger PendSV using SCB_ICSR_PENDSVSET_Msk. When PendSV fires, decideNextTask() saves outgoingSP and outgoingExcReturn to the active block, sets RUNNING tasks to READY, and runs a two-pass check over taskControlBlocks[]—evaluating high-priority TaskType::INTERRUPT tasks first, then TaskType::SCHEDULED tasks, and falling back to index 0 for idle. Since PendSV is the only place allowed to modify currTaskIndex, concurrent state corruption is structurally impossible."}, {"post_id": 71997, "ts": "2026-09-21T23:26:43Z", "hours": 7.2761, "text": "For the past couple weeks I have been mostly refining small bugs, which were originally rendering the RTOS usless, as well as refactoring the code for simplicity rather than elegancy. 1. Transition to Actual Hardware Renode is great for simulation, but it ended up hiding a bunch of race conditions in the population methods and the main scheduler that led to no code actually running during the IRQ handler calls. I spent a lot of time securing those resources and finally got the scheduler mostly finished up. Also, I decided to just take out the double preempts entirely since they were adding way too much complexity for absolutely zero speed benefits in practice (plus they were creating detrimental but rare race conditions). 2. Code Refactor To make things run a bit more consistent, I restructured how the control loops operate. I ended up merging the dShotGeneration task with the parent IMU control loop, so now everything from reading the sensors to pushing motor commands happens in one clean sequence (rather than including an unnecessary preempt). I also built out a brand new CRSF control loop to handle all the radio parsing and state machine updates in its own dedicated space (just for consistency). Lastly, while doing a final pass on the code, I realized that in my dShotGeneration helper, I had forgot to scale the values by a factor of 1/2000, as the inputs require the value to be between 0 and 1 (not 0 and 2000). 3. Final DMA Iteration The biggest relief was finally getting rid of the old blocking USART system. I finally transitioned away from manually handling the streaming by polling flags in a loop, which was tying up the processor way more than it should have. Everything has been converted over to a blocking DMA system, so the hardware can just handle all the memory transfers in the background while the CPU focuses on actually keeping the drone in the air. Overall, I think that this will be the final iteration of the RTOS DMA pipeline, as now that it is solid and works with all the edge cases, theres no need to change it. 4. Timeline With almost all the major bugs fixed, I am going to ship the project! While it is not 100% done, it will likely be as close as it will get during the timeframe of the Stardance hackathon, and with it so close, theres no reason not to ship. Anyway, I will keep the source as the main project rather than a release, so if I do end up fixing any major bugs, they will get patched either before or during project review (besides most of these bugs are only detectable on actual hardware). I will most likely finish the RTOS in the next couple weeks (by the end of October), and will finish the drivers for the ESC and RM by latest end of November (for more timing refer to the README). Thanks to everyone who supported me throughout this project (hopefully me and my friend can get this drone flying in another Hack Club Hackathon!)."}, {"post_id": 63202, "ts": "2026-09-06T21:25:57Z", "hours": 9.6606, "text": "1. Scheduler Hardening and Task Discovery Spent part of this session making the RTOS scheduler itself more robust. I added a findTaskByName() function so interrupt-driven task lookups happen dynamically instead of relying on hardcoded task indices, which was a fragile setup just waiting to break the moment task registration order changed. Alongside that, I wrapped every one of those lookups with lambdas that throw a proper warning if a task can’t be found, so a missing task fails loudly instead of silently dereferencing a null pointer somewhere downstream. 2. Blocking vs Non-Blocking Scheduled Tasks After a lot of painful debugging, I traced a string of issues (including interrupt tasks silently failing to fire and scheduled tasks stepping on each other) back to non-blocking statements I’d introduced inside scheduled tasks in the previous devlog. I ended up taking those out and reverting scheduled tasks fully back to blocking, which is genuinely what the scheduled zone was designed for in the first place: tasks that don’t need to run in polynomial time and can safely block without putting the drone at risk of crashing. Once that was sorted, I converted the raw USART byte-pushing into a blocking DMA system instead, so streaming is handled by DMA hardware rather than manually pushed byte-by-byte over USART. 3. Flight Loop Refactor and VTX Communication On the flight logic side, I refactored flightLoop() to use lambdas and a state machine so I would never have to yield in the middle of my tasks (which was used previousely for zeroing motor outputs). I also converted the error handling into a proper state machine, so different types of warnings can be caught and handled distinctly instead of lumping every issue into one path, and turned on full debug printing whenever the system is running. Last but not least, I built out a dedicated injector for respondToVTX so I can properly test and simulate VTX communication going forward. 4. Real Hardware Testing Since, I am almost 100% done with the RTOS I bought two NUCLEO-F756ZG’s which will serve as testing the 100% production code rather than the simulation version. I bought two so that one of the boards will be running the real RTOS code, testing DMA reading from USART, SPI, and emitting real motor signals, while the second will be emulating all of my peripherals. When the boards come, if the code does work, it will also mean that the real PCB’s will also work when my friend finishes with them. 5. Next Steps As of now there are a few issues with the double preempt system (for the interrupt tasks) I designed a while back, so I will need to debug that before they arrive. Right now before the boards arrive, I will be finishing up the RTOS blocking DMA pipelines (stripping out IRQ, removing NVIC etc. for scheduled tasks), and performing a bunch of baseline testing in order to prove that the RTOS is capable of flying an actual drone!"}, {"post_id": 52761, "ts": "2026-08-23T03:11:04Z", "hours": 9.6356, "text": "1. CLI Upgrades and Channel Mapping Over the past 5 days, I knocked out some major quality-of-life upgrades for the flight controller. I added customizable channel mapping for CRSF directly through the CLI. By utilizing a combination of references and pointers, I was able to point a volatile alias to certain channel mappings, allowing for completely customizable CRSF channels. While I was in the CLI logic, I also added the ability to configure custom IMU orientations around the z-axis, making it way easier to mount the board in different directions without having to recompile the firmware. 2. DMA Pipeline Fixes and Baro Optimization A big chunk of this session went into debugging and refactoring the hardware data pipelines. I adjusted the ADC population pipeline in order to fix some stream parsing issues, and gave the barometer a massive overhaul. The baro now uses a non-blocking, non-circular DMA setup that relies on a semi-interrupt-driven task call rather than a continuous loop. This enabled me to yield in the middle of the function, and call back later as soon as DMA is finished transferring. I also fixed a massive bug in the DMA setup: the DMA_SxCR_HTIE and DMA_SxCR_TCIE flags were accidentally enabled for the GPS and OSD ring buffers, which would have crashed the whole RTOS by firing unhandled interrupts, causing default HAL and IRQ handlers to run. 3. Walksnail Two-Way Communication Finally, I built out the respondToVTX task to get the drone properly communicating with the Walksnail VTX. Instead of just blindly sending video data out, this sets up true two-way communication over asynchronous UART. By writing a persistent byte-by-byte state machine, the flight controller can now safely parse incoming requests from the VTX without dropping half-packets, allowing me to send back live stick positions and armed status for the OSD menus. The only thing left to do now is handle persistent CLI storage, the logToBlackBox task, integrating the semi-interrupt pipeline with telemetryTX and the OSD, and a few other system tweaks. After that the RTOS will be 100% ready to fly the actual drone."}, {"post_id": 49360, "ts": "2026-08-18T04:16:35Z", "hours": 4.0211, "text": "Complete RTOS Overhaul, Circular DMA Pipelines, and CPU Abort Fixes This update focuses on fine-tuning the RTOS, organizing the scheduler, and rewriting microcontroller memory handling. Renode’s simulated DMA restrictions make software-only pipeline testing nearly impossible, so I plan to buy a physical dev board and Raspberry Pi to validate on silicon. While the Git diff might not look massive, the architecture has evolved from a blocking system to a true preemptive RTOS with precise polynomial timing. 1. Reworking DMA Flow & Standardizing Ring Buffers Sensor and receiver data pipelines are now driven by circular DMA, enabling non-blocking, high-frequency transfers with response times as fast as 0.3 ms (without printToUSART enabled). Everything now relies on ring buffers. Instead of generating complete packets instantly, mock injectors stream bytes directly into the buffers while updating the simulated NDTR register to mirror hardware behavior. if ( byteIdx >= frameLen ) { byteIdx = 0 ; } crsfRingBuffer [ head ] = frameBuf [ byteIdx ++ ]; head = ( head + 1 ) % CRSF_RING_BUFFER_SIZE ; currentBoardConfig . crsf_dma_stream -> NDTR = CRSF_RING_BUFFER_SIZE - head ; Feeding bytes incrementally moves the write head gradually, ensuring the simulation mimics a physical UART and allowing population functions to safely pull data within interrupt locks. 2. DMA Interrupts for Safe Parsing To prevent data overwrites, DMA streams trigger interrupts at the halfway (HTIE) and full-transfer (TCIE) points to queue tasks. Because registers like LISR are read-only in hardware, I implemented a software mock flag system (mock_LISR) for simulation: extern \"C\" void DMA1_Stream0_IRQHandler ( void ) { #ifdef SIMULATION uint32_t flags = mock_LISR ; mock_LISR = 0 ; #else uint32_t flags = DMA1 -> LISR ; #endif if ( flags & DMA_LISR_HTIF0 ) { DMA1 -> LIFCR = DMA_LIFCR_CHTIF0 ; decideNextInterruptTask ( & taskControlBlocks [ 1 ]); } } Functions safely extract data with interrupts disabled, ensuring the CPU never blocks waiting for peripheral transfers. 3. Assembly Bugs & CPU Aborts Debugging nested hardware interrupts exposed massive faults: The context-switching bug: FPU usage or large stack frames corrupted the return address, causing the CPU to execute BX LR with garbage data. Fixed in PendSV_Handler.S by explicitly storing excReturn inside the TaskControlBlock rather than relying on stacked registers. Second, a 0x00000000 Null Return Crash: Scheduled tasks attempted to return natively, popping 0x00000000 from the zero-initialized Link Register and causing a fetch fault. Every task now runs inside an infinite while(true) loop calling yieldCurrentTask() . 4. Clock PLLs & Hardware Re-routing After configuring the STM32CubeMX PLL for a 25 MHz HSE clock, the MCU runs at its full 216 MHz. I added a standardized initSystemGPIOClocks() function to enable peripheral clocks consistently. I also moved motor 3 from TIM1 to TIM4, synchronizing it with the 216MHz clock and freeing up a TIM1 DMA stream exclusively for the upcoming BlackBox logging. Future Updates With the core RTOS stabilized, development shifts to flight-facing systems: hooking the SPI pipeline to the video overlay for live OSD goggle telemetry, adding permanent flash storage for PID tunes/configs, exposing MCU controls via CLI, supporting programmable transmitter bind buttons, building a desktop GUI, and using the freed TIM1 DMA for BlackBox logging. Below, I have attached a screenshot of every single task working, and outputting values in the simulation, something that wasn’t possible for the interrupt tasks until now."}, {"post_id": 48647, "ts": "2026-08-17T03:57:01Z", "hours": 9.6761, "text": "1. Reworking the DMA Flow for Speed So, a lot has been going on lately, starting with a massive rework of the entire DMA flow. The functions are now entirely populated by circular DMA, which is a huge upgrade for the system’s overall performance. This architectural change allows for completely non-blocking, high-frequency calls, hitting response times as fast as 0.03 milliseconds. It basically means the whole data pipeline is significantly faster, and much more responsive than it was in previous versions. 2. Standardizing Ring Buffers and Mock Injectors To make everything more reliable, the DMA now follows a very specific and predictable pattern for both scheduled and interrupt tasks. Everything relies on ring buffers now, whether they are being injected by actual hardware (or simulated) DMA or by my custom simulation scripts. I also reworked the mock injectors so they no longer just return a pre-formatted packet. Instead, they write directly into the ring buffer, which lets me test the exact same parsing pipeline in software, simulating the hardware flow almost exactly like Renode (if only they would ever fix that USART DMA bug…). 3. Assembly BX LR Bug + STMCubeMX Clock PLL I also finally solved the long term BX LR return bug that I literally spent over 20 hours trying to find. Whenever tasks used the FPU or had slightly larger stacks, the return address in the r0 register would just get completely corrupted by the extra data. I fixed this by storing the return address in a dedicated uint value instead of blindly trusting the registers to hold it. That fix alone allowed me to safely re-add telemetryTX and make a bunch of other function logic much more robust. On top of that, I went into STM32CubeMX and reworked the clock PLL manually (it took over 3 hours to solve by hand). By feeding in a 25 MHz HSE clock, I managed to unlock the MCU’s internal clocks to run at their full 216 MHz.I also took some time to clean up the codebase to improve overall readability and commenting. 4. DMA Interrupts for Safe Parsing Another feature is that DMA now triggers interrupts when the ring buffers hit exactly halfway and when they are fully populated. This gives me the time to parse all the incoming values safely without having the risk about data getting overwritten by the next DMA pass before I am done reading it. I set up the IRQ handlers in the code to call the interrupt methods automatically, which ensures the CPU stays completely unblocked and free to handle other flight control tasks almost all the time (overall decreasing headroom). 5. Current Status: NVIC Bug Sadly, the code is actually not functional right now because the NVIC and IRQ pipeline is malfunctioning and just refusing to call the interrupts for reasons I cannot figure out yet. Because of this, I am holding off on pushing the code until I can track down the bug, give everything a final polish, and run some benchmark testing (since I don’t want to commit broken code). Also, sorry for promising a commit a day or two after my last devlog. I got super busy, especially with vacation, but I will try my best to get this sorted. Depending on how long it takes to figure out this NVIC issue, the next commit might drop anywhere from tomorrow to a few weeks from now."}, {"post_id": 41654, "ts": "2026-08-06T00:44:12Z", "hours": 9.5336, "text": "This week was heavily focused on the fine tuning and organization of the RTOS. I spent numerous hours of research and untangling environment setups. I originally tried to build out a complete framework for the drone code without relying on any simulation code, but Renode’s restrictions, specifically with its ability to perform DMA requests, quickly made it clear that path was impossible. To fix this I’m looking into picking up a dev board and a Raspberry Pi so I can test and debug everything on an actual MCU before finally moving the codebase over to the drone. Even though the git won’t look a whole lot different and not a lot of actual code changed, getting these configs finalized (especially in STM32CubeMX) in was a huge step forward for the project. I expect to commit the next version of the code either today or tomorrow, with improvements specifically in TelemetryTX, BlackBoxLogging, and a few other minor changes!"}, {"post_id": 37983, "ts": "2026-07-31T02:30:16Z", "hours": 8.6756, "text": "Major Architecture Upgrade : OSD, Betaflight Rates & Build System Refactor 1. OSD, DMA + SYS First off on the hardware side, I added native MSP-based OSD support for Walksnail video transmitters, built the SPI barometer driver for altitude tracking, and spun up a low-level health task that constantly monitors MPU registers and stack usage in the background. On top of that, I finally fixed the DMA stream conflicts (mainly in STMCubeMX) between TIM1 DShot motor outputs , ADC1, and SPI peripherals, so everything runs without collisions now. 2. SYS Health + Debugging I also implemented a few other low level tasks, allowing me to monitor system health, based upon ram usage and storage used. This task prints out the amount at which each tasks stack is being used, allowing me to effectively debug whether a task was actually overflowing, vs an unrestricted read/write. Furthermore, the original canary values that I used, were completely replaced as they in fact were causing some of the “stack overflow” bugs (via unrestricted reads/write every 1 ms in SysTick_Handler ). 3. Betaflight Rates + CLI Update For flight feel, I implemented Betaflight’s Actual Rates algorithm with custom expo curves, so stick inputs map to target angular velocities with center-stick precision. I also completely separated serial communications by moving the interactive CLI to USART1 (accessible via nc -C localhost 1234 in terminal) and pushing system logs to USART3. Additionally, I removed the old telemetryTX task because its unauthorized reads/writes were completely breaking the RTOS scheduler, and expanded the CLI with a ton of commands for live sensor readings and more PID tuning. Speaking of which, I am planning to create a persistent file in /bin/ , allowing for rates to be stored between runs, rather than being reset each during runtime, and having to be manually configured through the CLI. 4. Overhauled Building Workflow Finally, I overhauled the whole developer workflow and build automation setup. The build.sh and build.bat scripts now auto-configure custom CMake profiles and generate a full disassembly file in /bin/ after every build for fast low-level debugging, while run.sh and run.bat automatically launch the right terminal windows on launch. 5. Future Additions: I’m planning to build a closed-loop Renode simulation matrix to stream hardware data into every peripheral externally, letting me run 100% real production code during SIL testing! For this, I will utilize the currently blank simulate.py as well as certain commands in simulate.resc to create properly formatted dynamic buffers(which are not directly injected into the code), allowing me to properly test the DMA drivers, and many of the memory management helpers."}, {"post_id": 35605, "ts": "2026-07-27T04:22:30Z", "hours": 9.44, "text": "Over the last few days, I wrapped up two major tasks for the flight controller’s core sensor and telemetry stack: the on-screen display (OSD) renderer and the BMP390 barometer subsystem. The OSD engine is now fully hooked up to the state system, giving real-time visual downlinks for critical metrics like flight battery voltage, active flight modes, and height statistics over UART. To keep the execution completely non-blocking, all frame updates are dispatched asynchronously, preventing rendering delays from hindering the performance of the high-frequency control loops. On the barometer side, I implemented altitude tracking using Bosch’s BMP390 sensor over SPI with non-blocking DMA burst transfers. In the 50Hz task loop, incoming 24-bit raw pressure and temperature bytes are assembled and passed through the hypsometric formula to calculate smooth, relative height above ground level (AGL). Although the barometer task is a bit flawed due to floating point register stack errors with the header and end canary values, they don’t seem to be affecting the system in anyway (nor is any data being corrupted). I will find a way to fix these issues before the next commit, as well as the race condition in the interrupt tasks in the next Github commit. After that, it is just system polish and extensive testing before the RTOS will be complete!"}, {"post_id": 33865, "ts": "2026-07-24T04:03:21Z", "hours": 7.1808, "text": "1. Fixing the Stack Overflows at Boot After an entire 25-hour debugging period across 4 days, the RTOS kernel is finally done (for the scheduled tasks). I tracked down a silent bug where several critical scheduled tasks—like powerManagement , lowLevelFailSafe , and telemetryTX —were crashing the MCU on boot. The issue came down to a boot-time dependency lock: tasks were trying to poll hardware registers (like UART TX flags) and read shared drone state variables before the peripherals or producer tasks had time to initialize on Tick 0. By adding an initial yieldCurrentTask() right at the top of these methods, Task Phase Alignment was officially added. This gives the hardware a full cycle to stabilize, preventing buffer overflows and eliminating the cascading task state corruptions that were locking up the scheduler. 2. Custom Stack Sizing & Real-Time Guard Checks Alongside the scheduler fix, I pushed major upgrades to memory safety and task management. Instead of assigning a static stack array to every task, I transitioned to dynamic per-task stack allocations during initialization ( new (std::nothrow) ). Heavy string-parsing and math tasks get larger stacks, while simple toggle tasks stay lightweight. To catch memory issues before they corrupt the heap, I implemented a custom dual guarding system: 0xDEADBEEF is appended at the bottom of the stack to catch standard stack overflows, while a new 0xBEEFCAFE canary value is written to the very top index ( stackSizeWords ) to instantly flag memory breaches in the SysTick_Handler . Furthermore, I refactored powerManagement to use ADC2 in Circular DMA Mode, writing battery telemetry directly to RAM in the background for zero CPU overhead. 3. Transitioning to Walksnail Avatar HD Looking ahead, my and my friend are officially transitioning our existing analog video setup over to a fully integrated Walksnail Avatar HD digital ecosystem. Rather than keeping my custom telemetry task ( telemetryTX ) with heavy string formatting over raw UART, Walksnail allows me to stream standard MSP (MultiWii Serial Protocol) DisplayPort frames straight to the VTX. The Walksnail system will serve as our primary onboard logging source, giving us full OSD telemetry, high-framerate HD video, and precise blackbox data logging. With the RTOS core now stable and non-blocking, our next focus shifts toward “premium” flight controller features, e.g. including custom RC stick expo curves, an expanded USB CLI, and an integrated simulator interface."}, {"post_id": 32665, "ts": "2026-07-21T23:24:15Z", "hours": 9.0794, "text": "I just finished expanding the RTOS architecture, filling out all of the scheduled background tasks to handle real-time flight telemetry, peripheral hardware, and terminal configuration. On the telemetry side, I built out telemetryTX to package battery, status, and GPS data into standard CRSF frames over UART, alongside an NMEA gpsParser that decodes $GNGGA and $GNRMC sentences for live location fixes. I also made powerManagement into its own standalone task so ADC voltage and current metrics can feed into the failsafe logic and telemetry concurrently. To make bench testing and tuning significantly easier, I added usbCLI over USART1, giving me a full interactive shell to tweak PID gains (Kp, Ki, Kd), dump raw IMU data, and inspect hardware states on the fly without needing to reflash firmware. To keep the physical quad safe and responsive during flight, I added updatePeripherals for LED status patterns and buzzer warnings, along with a dedicated Independent Watchdog (IWDG) task running as a low-priority fail-safe to trigger hardware MCU resets if the scheduler ever freezes. On the project structure side, CMake now routes all output binaries straight into a clean ./bin/ directory so nobody has to compile the source manually just to run the .elf target in Renode. I also introduced a -DENABLE_DEBUG flag, allowing me to cleanly toggle between complete USART log prints across every loop or a blank terminal environment meant purely for the CLI shell. While all the background subsystems and scheduled task priorities are fully mapped out, I’m currently figuring out a few lingering RTOS bugs—specifically around stack allocation, causing stack overflow flags on certain scheduled tasks, along with scheduling starvation where context switching isn’t releasing task blocks properly. Because I want to ensure the scheduler kernel is 100% complete before pushing broken stack frames to the main branch, the GitHub repository will be updated with the complete, bug-fixed code later today. Everything is moving in the right direction, and getting these background tasks finalized brings me one step closer to full flight hardware deployment!"}, {"post_id": 31067, "ts": "2026-07-19T04:58:26Z", "hours": 2.5139, "text": "I’ve spent the last 20 hours of coding basically staring at bare-metal memory allocations and tracking down why the drone’s context switcher was triggering CPU drops under the Renode simulation environment. Moving away from manual Python injection scripts to a cleaner C++ conditional compilation framework using CMake was supposed to simplify the testing layout, but instead, it exposed structural design flaws in how the scheduler kernel boots up. Running a multi-sample sensorCalibration() routine at cold boot was forcing hidden task yields before the OS kernel was fully mapped out, which was throwing unmapped register address errors and wiping out index-0 entries. To solve this, I decoupled the entire state machine sequence so that hardware allocations, peripheral registers, and core tracking counters now clear before any operational loops drop into the registry. I also added a critical rtosStarted gatekeeper flag to yieldCurrentTask() so calibration math runs cleanly in sequence without triggering early PendSV handler switches. On top of that, the task stack pointers are completely realigned. The assembly context swapper now maps a precise 32-word boundary allocation utilizing 0xFFFFFFFD as a basic exception return code, which completely eliminates the data corruption that was misaligning our Process Stack Pointer. With the fundamental kernel stabilized, I shifted entirely into clean architecture organization. The massive, cluttered code blocks have been split up into independent tracking files (BufferPopulation.cpp, Tasks.cpp, and SimInjector.cpp). High-speed SPI reads for the IMU, UART ring buffers for decoding CRSF radio channel frames, battery ADC sampling, and DShot600 motor output mixers are now fully broken out and functional. There is still a nagging preemption lockout issue inside the simulator where a single interrupt task attempts to starve out the other concurrent loops during a millisecond window, but the baseline operating system is locked in, meaning I can confidently verify the PID flight mixing tables and safety code blocks safely before the code ever touches a physical quadcopter. If you would like to run your own tasks, I also added support in the .bat and .sh files to build code for the simulation (flag=sim), user task code (flag=user), and actual drone upload code (no flags). Example MacOS Commands: ./build.sh sim ./build.sh user ./build.sh This makes it so that I don’t have to constantly modify code to run either the user tasks or simulation code, instead using a flag. Overall, this RTOS is not done yet, however, I plan to add the rest of the scheduled tasks later today, and fixing the sole bug keeping this simulation back! I expect a full ship to be comming out in the next week or so, along with all ESC, RM, and TM code complete!"}, {"post_id": 30963, "ts": "2026-07-18T23:42:07Z", "hours": 9.2461, "text": "I’ve been doing a lot of debugging for the RTOS scheduler inside the simulation environment, and it’s been very difficult for me to identify many bugs in teh core architecture of my tasks. Moving away from Python scripts to a cleaner C++ conditional compilation framework using CMake was supposed to simplify things, but instead, it exposed some nasty architectural bugs in how tasks and context switching interact. There has also been a massive priority inversion and preemption lockup where the CRSF parsing loop completely starves out the lower-priority threads. Also, running a raw sensor calibration loop at boot was forcing thread switches before the OS kernel was even fully initialized, completely corrupting task indexes, throwing null pointer warnings, and leaving half of my PID calculations at zero. Still working on many fixes, but it has been a lot of work (9 hours), but I hope to get the full RTOS with at least bare minimum flight tasks out by tomorrow! The GitHub repo still won’t be fully up-to-date until these structural bugs are figured out, but I plan to push the corrected scheduler files and mock drivers very soon. The screenshot below shows an image of the CRSF task completely dominating, without any processes interrupting it."}, {"post_id": 30245, "ts": "2026-07-17T17:45:35Z", "hours": 9.5911, "text": "I just wrapped up the core architecture of the drone’s flight controller, including mapping out all 7 interrupt methods into a far more optimized RTOS scheduler. The updated version makes use of a FIFO stack, to keep track of nested interrupts. I also protected the main code from data corruption during pendSV switches by enabling and disabling irq in certain areas of vulnerable code. Other small changes to the assembly to save floating point registers as well as better memory allocation was also done in this update. From high-speed IMU sensor integration and state estimation calculations to CRSF packet decoding and DShot motor generation, the essential tasks are fully written and compiled. I’ve officially shifted gears into the testing phase, working on simulated environments to debug DMA transfers, registers, and the flight state machine’s arming sequences. As of now, I am writing python scripts by hand, in order to inject “dummy” values into the GPIO, SPI, and USART registers in order to populate the buffers, and simulate the calculations. As the code is becoming longer and longer, I will try to refactor a lot of the files into smaller more organized portions, to increase readability (often I have a hard time traversing everything). Note: The current code pushed to GitHub isn’t quite the latest since I’m in the middle of troubleshooting a few simulation-specific register bugs, but I wanted to get this update out to meet the 10-hour devlog requirement on Stardance. Everything is moving in the right direction, and having a virtual environment all working means I can safely verify the PID mixer and safety flags before this code ever touches a physical quad. This officially marks the end of both phases 2 and 3, and now its a matter of finishing extra scheduled telemetry tasks (e.g. OSD and EdgeTX logging), but I estimate that the flight controller portion of the code (around 60% of all logic in the drone) will be done between now and Sunday, July 19th! I will also post a much more in depth devlog about how the methods work, as well as an updated README.md on Github ASAP."}, {"post_id": 28703, "ts": "2026-07-15T03:44:43Z", "hours": 1.4603, "text": "To make my hybrid RTOS accessible and easy to test, I designed a modular sandbox task registry system that isolates the core kernel from custom user code. Developers can write standard non-returning tasks in an isolated file ( User_Tasks/user_tasks.cpp ), while the kernel exposes a registration hook that allocates a dedicated 1024-word stack space. This decoupling means anyone can plug in their own cooperative or preemptive tasks, set execution periods or trigger bounds, and run them instantly on our virtual STM32F7 without risking memory corruption or touching the underlying context-switching assembly. For more information on how to make your own tasks refer to the README.md ."}, {"post_id": 28651, "ts": "2026-07-15T01:05:47Z", "hours": 1.0922, "text": "I just updated a few build files and restructured the project, so code and simulation separation would be better. I also added a few executable files (build.sh, run.bat, and run.sh) to easily compile and run the project."}, {"post_id": 28615, "ts": "2026-07-14T23:38:20Z", "hours": 5.5942, "text": "I finally reached a massive milestone with the kernel architecture and got the scheduler firing exactly how it needs to. Even though I thought the RTOS portion was completely wrapped up a bit ago, now it is officially done. I ended up doing a massive revamp on how scheduled tasks execute, routing them through PendSV so that the background worker states get completely backed up and saved by my assembly code just like the preemptive ones. I also cleaned up the initialization API by throwing in an overloaded function to separate timed loops from raw hardware events (the interrupt tasks). Overall most of the testing logic is now gone, and now the system is being more and more focused on the actual drone tasks. On the hardware side of things, I built out the actual drone tasks and the underlying SPI and UART DMA drivers that populate my local data packets. Even though they are not in use right now as all the tasks (which are not fully completed) require the support of each other for full testing. With the core OS completely checked off, the focus is shifting entirely over to the actual flight control logic. Over the next couple of days, I’m going to wrap up the final flight controller tasks, and get the core stabilization algorithms running. Once the flight controller application layer is fully put together, I’m moving straight onto building out complete simulations for the receiver, transmitter, and finally the ESC modules to get the whole drone network interacting together!"}, {"post_id": 27365, "ts": "2026-07-12T21:17:48Z", "hours": 3.8289, "text": "I finally finished the most difficult part of the project so far and got a hybrid cooperative/preemptive scheduler running from scratch. After a ton of debugging with the assembly memory layout, the core RTOS kernel shell is officially stable, verified inside the Renode simulator, and completely ready to handle flight data. The architecture divides the CPU’s brain into two execution pathways to maximize calculation speeds while keeping latency near zero. Standard background tasks, like handling low-priority peripheral updates, run sequentially inside a cooperative loop based on their relative priority. They execute and then drop out without wasting an overhead time. But for high-priority hardware event such as a critical radio packet arriving over UART or raw sensor data from the IMU, the system completely bypasses the standard queue. It pulls a physical hardware lever (PendSV) that forces an instant stack swap. When an interrupt task fires through PendSV, the CPU pauses the background scheduled loop, and my assembly code packs up the core registers, saves that exact memory address to RAM, and hot-swaps the Process Stack Pointer to launch the interrupt task. Right now, there is just a dummy delay loop inside the interrupt task to visually show the execution break in the console logs. Once it finishes printing, it hits a yield function that throws the assembly switch backward, pops the background registers, and hands control straight back to the flight loop. The repository currenly has a lot of diagnostic test code because I needed to prove the pointer indirection and stack-faking logic worked without blowing up the memory alignment. Now that the plumbing is completely verified, the next step is to strip out the test code and start writing the actual flight infrastructure. I am moving straight into coding the PID control loops, signal mixers, Kalman filters, and live motor generation."}, {"post_id": 26996, "ts": "2026-07-12T03:54:25Z", "hours": 2.8228, "text": "I finally added minimum task scheduling for the RTOS. I had originally designed a simple kernel to create a buffer of scheduled tasks in QEMU, but after learning that the simulation didn’t actually support the specific MCU that my friend and I are using for the real drone, I had to pivot. I switched over to using STM32CubeMX for the actual bare-metal configuration and Renode to handle running the emulation. Right now, the kernel just executes a task based on a basic timing loop and priority, waits for it to completely finish, and then moves on to the next task. To be honest, it isn’t super useful yet because it only covers tasks in what I call the “scheduled zone” of the drone. This includes background stuff like blackbox flight data management, but it leaves out the most critical parts like the PID control loops and streaming real-time UART data from the IMU. Those features need to fire their own asynchronous interrupt signals at the highest possible hardware priority so they can instantly pause the optional background tasks and execute the critical code that actually keeps the drone stable and flying. Next up, I need to write a short piece of assembly to handle saving the current CPU state and registers of whatever task is currently running when an interrupt hits. It shouldn’t be too bad since it will only take about 30 lines of code. I know I haven’t been posting devlogs for the past couple of weeks even though I’ve been grinding on this, but that’s mostly because we spent a ton of time initially planning out the architecture of the drone. We wanted to map out the software boundaries early so my friend and I could go our separate ways working on different systems without having massive merge conflicts later on."}]...
|
Title: 200 MPH Drone RTOS by @Guagyyy | Stardance
Title: 200 MPH Drone RTOS by @Guagyyy | Stardance
Devlogs:
I finally added minimum task scheduling for the RTOS. I had originally designed a simple kernel to create a buffer of scheduled tasks in QEMU, but after learning that the simulation didn’t actually support the specific MCU that my friend and I are using for the real drone, I had to pivot. I switched over to using STM32CubeMX for the actual bare-metal configuration and Renode to handle running the emulation. Right now, the kernel just executes a task based on a basic timing loop and priority, waits for it to completely finish, and then moves on to the next task. To be honest, it isn’t super useful yet because it only covers tasks in what I call the “scheduled zone” of the drone. This includes background stuff like blackbox flight data management, but it leaves out the most critical parts like the PID control loops and streaming real-time UART data from the IMU. Those features need to fire their own asynchronous interrupt signals at the highest possible hardware priority so they can instantly pause the optional background tasks and execute the critical code that actually keeps the drone stable and flying. Next up, I need to write a short piece of assembly to handle saving the current CPU state and registers of whatever task is currently running when an interrupt hits. It shouldn’t be too bad since it will only take about 30 lines of code. I know I haven’t been posting devlogs for the past couple of weeks even though I’ve been grinding on this, but that’s mostly because we spent a ton of time initially planning out the architecture of the drone. We wanted to map out the software boundaries early so my friend and I could go our separate ways working on different systems without having massive merge conflicts later on.
I finally finished the most difficult part of the project so far and got a hybrid cooperative/preemptive scheduler running from scratch. After a ton of debugging with the assembly memory layout, the core RTOS kernel shell is officially stable, verified inside the Renode simulator, and completely ready to handle flight data. The architecture divides the CPU’s brain into two execution pathways to maximize calculation speeds while keeping latency near zero. Standard background tasks, like handling low-priority peripheral updates, run sequentially inside a cooperative loop based on their relative priority. They execute and then drop out without wasting an overhead time. But for high-priority hardware event such as a critical radio packet arriving over UART or raw sensor data from the IMU, the system completely bypasses the standard queue. It pulls a physical hardware lever (PendSV) that forces an instant stack swap. When an interrupt task fires through PendSV, the CPU pauses the background scheduled loop, and my assembly code packs up the core registers, saves that exact memory address to RAM, and hot-swaps the Process Stack Pointer to launch the interrupt task. Right now, there is just a dummy delay loop inside the interrupt task to visually show the execution break in the console logs. Once it finishes printing, it hits a yield function that throws the assembly switch backward, pops the background registers, and hands control straight back to the flight loop. The repository currenly has a lot of diagnostic test code because I needed to prove the pointer indirection and stack-faking logic worked without blowing up the memory alignment. Now that the plumbing is completely verified, the next step is to strip out the test code and start writing the actual flight infrastructure. I am moving straight into coding the PID control loops, signal mixers, Kalman filters, and live motor generation.
I finally reached a massive milestone with the kernel architecture and got the scheduler firing exactly how it needs to. Even though I thought the RTOS portion was completely wrapped up a bit ago, now it is officially done. I ended up doing a massive revamp on how scheduled tasks execute, routing them through PendSV so that the background worker states get completely backed up and saved by my assembly code just like the preemptive ones. I also cleaned up the initialization API by throwing in an overloaded function to separate timed loops from raw hardware events (the interrupt tasks). Overall most of the testing logic is now gone, and now the system is being more and more focused on the actual drone tasks. On the hardware side of things, I built out the actual drone tasks and the underlying SPI and UART DMA drivers that populate my local data packets. Even though they are not in use right now as all the tasks (which are not fully completed) require the support of each other for full testing. With the core OS completely checked off, the focus is shifting entirely over to the actual flight control logic. Over the next couple of days, I’m going to wrap up the final flight controller tasks, and get the core stabilization algorithms running. Once the flight controller application layer is fully put together, I’m moving straight onto building out complete simulations for the receiver, transmitter, and finally the ESC modules to get the whole drone network interacting together!
I just updated a few build files and restructured the project, so code and simulation separation would be better. I also added a few executable files (build.sh, run.bat, and run.sh) to easily compile and run the project.
To make my hybrid RTOS accessible and easy to test, I designed a modular sandbox task registry system that isolates the core kernel from custom user code. Developers can write standard non-returning tasks in an isolated file ( User_Tasks/user_tasks.cpp ), while the kernel exposes a registration hook that allocates a dedicated 1024-word stack space. This decoupling means anyone can plug in their own cooperative or preemptive tasks, set execution periods or trigger bounds, and run them instantly on our virtual STM32F7 without risking memory corruption or touching the underlying context-switching assembly. For more information on how to make your own tasks refer to the README.md .
I just wrapped up the core architecture of the drone’s flight controller, including mapping out all 7 interrupt methods into a far more optimized RTOS scheduler. The updated version makes use of a FIFO stack, to keep track of nested interrupts. I also protected the main code from data corruption during pendSV switches by enabling and disabling irq in certain areas of vulnerable code. Other small changes to the assembly to save floating point registers as well as better memory allocation was also done in this update. From high-speed IMU sensor integration and state estimation calculations to CRSF packet decoding and DShot motor generation, the essential tasks are fully written and compiled. I’ve officially shifted gears into the testing phase, working on simulated environments to debug DMA transfers, registers, and the flight state machine’s arming sequences. As of now, I am writing python scripts by hand, in order to inject “dummy” values into the GPIO, SPI, and USART registers in order to populate the buffers, and simulate the calculations. As the code is becoming longer and longer, I will try to refactor a lot of the files into smaller more organized portions, to increase readability (often I have a hard time traversing everything). Note: The current code pushed to GitHub isn’t quite the latest since I’m in the middle of troubleshooting a few simulation-specific register bugs, but I wanted to get this update out to meet the 10-hour devlog requirement on Stardance. Everything is moving in the right direction, and having a virtual environment all working means I can safely verify the PID mixer and safety flags before this code ever touches a physical quad. This officially marks the end of both phases 2 and 3, and now its a matter of finishing extra scheduled telemetry tasks (e.g. OSD and EdgeTX logging), but I estimate that the flight controller portion of the code (around 60% of all logic in the drone) will be done between now and Sunday, July 19th! I will also post a much more in depth devlog about how the methods work, as well as an updated README.md on Github ASAP.
I’ve been doing a lot of debugging for the RTOS scheduler inside the simulation environment, and it’s been very difficult for me to identify many bugs in teh core architecture of my tasks. Moving away from Python scripts to a cleaner C++ conditional compilation framework using CMake was supposed to simplify things, but instead, it exposed some nasty architectural bugs in how tasks and context switching interact. There has also been a massive priority inversion and preemption lockup where the CRSF parsing loop completely starves out the lower-priority threads. Also, running a raw sensor calibration loop at boot was forcing thread switches before the OS kernel was even fully initialized, completely corrupting task indexes, throwing null pointer warnings, and leaving half of my PID calculations at zero. Still working on many fixes, but it has been a lot of work (9 hours), but I hope to get the full RTOS with at least bare minimum flight tasks out by tomorrow! The GitHub repo still won’t be fully up-to-date until these structural bugs are figured out, but I plan to push the corrected scheduler files and mock drivers very soon. The screenshot below shows an image of the CRSF task completely dominating, without any processes interrupting it.
I’ve spent the last 20 hours of coding basically staring at bare-metal memory allocations and tracking down why the drone’s context switcher was triggering CPU drops under the Renode simulation environment. Moving away from manual Python injection scripts to a cleaner C++ conditional compilation framework using CMake was supposed to simplify the testing layout, but instead, it exposed structural design flaws in how the scheduler kernel boots up. Running a multi-sample sensorCalibration() routine at cold boot was forcing hidden task yields before the OS kernel was fully mapped out, which was throwing unmapped register address errors and wiping out index-0 entries. To solve this, I decoupled the entire state machine sequence so that hardware allocations, peripheral registers, and core tracking counters now clear before any operational loops drop into the registry. I also added a critical rtosStarted gatekeeper flag to yieldCurrentTask() so calibration math runs cleanly in sequence without triggering early PendSV handler switches. On top of that, the task stack pointers are completely realigned. The assembly context swapper now maps a precise 32-word boundary allocation utilizing 0xFFFFFFFD as a basic exception return code, which completely eliminates the data corruption that was misaligning our Process Stack Pointer. With the fundamental kernel stabilized, I shifted entirely into clean architecture organization. The massive, cluttered code blocks have been split up into independent tracking files (BufferPopulation.cpp, Tasks.cpp, and SimInjector.cpp). High-speed SPI reads for the IMU, UART ring buffers for decoding CRSF radio channel frames, battery ADC sampling, and DShot600 motor output mixers are now fully broken out and functional. There is still a nagging preemption lockout issue inside the simulator where a single interrupt task attempts to starve out the other concurrent loops during a millisecond window, but the baseline operating system is locked in, meaning I can confidently verify the PID flight mixing tables and safety code blocks safely before the code ever touches a physical quadcopter. If you would like to run your own tasks, I also added support in the .bat and .sh files to build code for the simulation (flag=sim), user task code (flag=user), and actual drone upload code (no flags). Example MacOS Commands: ./build.sh sim ./build.sh user ./build.sh This makes it so that I don’t have to constantly modify code to run either the user tasks or simulation code, instead using a flag. Overall, this RTOS is not done yet, however, I plan to add the rest of the scheduled tasks later today, and fixing the sole bug keeping this simulation back! I expect a full ship to be comming out in the next week or so, along with all ESC, RM, and TM code complete!
I just finished expanding the RTOS architecture, filling out all of the scheduled background tasks to handle real-time flight telemetry, peripheral hardware, and terminal configuration. On the telemetry side, I built out telemetryTX to package battery, status, and GPS data into standard CRSF frames over UART, alongside an NMEA gpsParser that decodes $GNGGA and $GNRMC sentences for live location fixes. I also made powerManagement into its own standalone task so ADC voltage and current metrics can feed into the failsafe logic and telemetry concurrently. To make bench testing and tuning significantly easier, I added usbCLI over USART1, giving me a full interactive shell to tweak PID gains (Kp, Ki, Kd), dump raw IMU data, and inspect hardware states on the fly without needing to reflash firmware. To keep the physical quad safe and responsive during flight, I added updatePeripherals for LED status patterns and buzzer warnings, along with a dedicated Independent Watchdog (IWDG) task running as a low-priority fail-safe to trigger hardware MCU resets if the scheduler ever freezes. On the project structure side, CMake now routes all output binaries straight into a clean ./bin/ directory so nobody has to compile the source manually just to run the .elf target in Renode. I also introduced a -DENABLE_DEBUG flag, allowing me to cleanly toggle between complete USART log prints across every loop or a blank terminal environment meant purely for the CLI shell. While all the background subsystems and scheduled task priorities are fully mapped out, I’m currently figuring out a few lingering RTOS bugs—specifically around stack allocation, causing stack overflow flags on certain scheduled tasks, along with scheduling starvation where context switching isn’t releasing task blocks properly. Because I want to ensure the scheduler kernel is 100% complete before pushing broken stack frames to the main branch, the GitHub repository will be updated with the complete, bug-fixed code later today. Everything is moving in the right direction, and getting these background tasks finalized brings me one step closer to full flight hardware deployment!
1. Fixing the Stack Overflows at Boot After an entire 25-hour debugging period across 4 days, the RTOS kernel is finally done (for the scheduled tasks). I tracked down a silent bug where several critical scheduled tasks—like powerManagement , lowLevelFailSafe , and telemetryTX —were crashing the MCU on boot. The issue came down to a boot-time dependency lock: tasks were trying to poll hardware registers (like UART TX flags) and read shared drone state variables before the peripherals or producer tasks had time to initialize on Tick 0. By adding an initial yieldCurrentTask() right at the top of these methods, Task Phase Alignment was officially added. This gives the hardware a full cycle to stabilize, preventing buffer overflows and eliminating the cascading task state corruptions that were locking up the scheduler. 2. Custom Stack Sizing & Real-Time Guard Checks Alongside the scheduler fix, I pushed major upgrades to memory safety and task management. Instead of assigning a static stack array to every task, I transitioned to dynamic per-task stack allocations during initialization ( new (std::nothrow) ). Heavy string-parsing and math tasks get larger stacks, while simple toggle tasks stay lightweight. To catch memory issues before they corrupt the heap, I implemented a custom dual guarding system: 0xDEADBEEF is appended at the bottom of the stack to catch standard stack overflows, while a new 0xBEEFCAFE canary value is written to the very top index ( stackSizeWords ) to instantly flag memory breaches in the SysTick_Handler . Furthermore, I refactored powerManagement to use ADC2 in Circular DMA Mode, writing battery telemetry directly to RAM in the background for zero CPU overhead. 3. Transitioning to Walksnail Avatar HD Looking ahead, my and my friend are officially transitioning our existing analog video setup over to a fully integrated Walksnail Avatar HD digital ecosystem. Rather than keeping my custom telemetry task ( telemetryTX ) with heavy string formatting over raw UART, Walksnail allows me to stream standard MSP (MultiWii Serial Protocol) DisplayPort frames straight to the VTX. The Walksnail system will serve as our primary onboard logging source, giving us full OSD telemetry, high-framerate HD video, and precise blackbox data logging. With the RTOS core now stable and non-blocking, our next focus shifts toward “premium” flight controller features, e.g. including custom RC stick expo curves, an expanded USB CLI, and an integrated simulator interface.
Over the last few days, I wrapped up two major tasks for the flight controller’s core sensor and telemetry stack: the on-screen display (OSD) renderer and the BMP390 barometer subsystem. The OSD engine is now fully hooked up to the state system, giving real-time visual downlinks for critical metrics like flight battery voltage, active flight modes, and height statistics over UART. To keep the execution completely non-blocking, all frame updates are dispatched asynchronously, preventing rendering delays from hindering the performance of the high-frequency control loops. On the barometer side, I implemented altitude tracking using Bosch’s BMP390 sensor over SPI with non-blocking DMA burst transfers. In the 50Hz task loop, incoming 24-bit raw pressure and temperature bytes are assembled and passed through the hypsometric formula to calculate smooth, relative height above ground level (AGL). Although the barometer task is a bit flawed due to floating point register stack errors with the header and end canary values, they don’t seem to be affecting the system in anyway (nor is any data being corrupted). I will find a way to fix these issues before the next commit, as well as the race condition in the interrupt tasks in the next Github commit. After that, it is just system polish and extensive testing before the RTOS will be complete!
Major Architecture Upgrade : OSD, Betaflight Rates & Build System Refactor 1. OSD, DMA + SYS First off on the hardware side, I added native MSP-based OSD support for Walksnail video transmitters, built the SPI barometer driver for altitude tracking, and spun up a low-level health task that constantly monitors MPU registers and stack usage in the background. On top of that, I finally fixed the DMA stream conflicts (mainly in STMCubeMX) between TIM1 DShot motor outputs , ADC1, and SPI peripherals, so everything runs without collisions now. 2. SYS Health + Debugging I also implemented a few other low level tasks, allowing me to monitor system health, based upon ram usage and storage used. This task prints out the amount at which each tasks stack is being used, allowing me to effectively debug whether a task was actually overflowing, vs an unrestricted read/write. Furthermore, the original canary values that I used, were completely replaced as they in fact were causing some of the “stack overflow” bugs (via unrestricted reads/write every 1 ms in SysTick_Handler ). 3. Betaflight Rates + CLI Update For flight feel, I implemented Betaflight’s Actual Rates algorithm with custom expo curves, so stick inputs map to target angular velocities with center-stick precision. I also completely separated serial communications by moving the interactive CLI to USART1 (accessible via nc -C localhost 1234 in terminal) and pushing system logs to USART3. Additionally, I removed the old telemetryTX task because its unauthorized reads/writes were completely breaking the RTOS scheduler, and expanded the CLI with a ton of commands for live sensor readings and more PID tuning. Speaking of which, I am planning to create a persistent file in /bin/ , allowing for rates to be stored between runs, rather than being reset each during runtime, and having to be manually configured through the CLI. 4. Overhauled Building Workflow Finally, I overhauled the whole developer workflow and build automation setup. The build.sh and build.bat scripts now auto-configure custom CMake profiles and generate a full disassembly file in /bin/ after every build for fast low-level debugging, while run.sh and run.bat automatically launch the right terminal windows on launch. 5. Future Additions: I’m planning to build a closed-loop Renode simulation matrix to stream hardware data into every peripheral externally, letting me run 100% real production code during SIL testing! For this, I will utilize the currently blank simulate.py as well as certain commands in simulate.resc to create properly formatted dynamic buffers(which are not directly injected into the code), allowing me to properly test the DMA drivers, and many of the memory management helpers.
This week was heavily focused on the fine tuning and organization of the RTOS. I spent numerous hours of research and untangling environment setups. I originally tried to build out a complete framework for the drone code without relying on any simulation code, but Renode’s restrictions, specifically with its ability to perform DMA requests, quickly made it clear that path was impossible. To fix this I’m looking into picking up a dev board and a Raspberry Pi so I can test and debug everything on an actual MCU before finally moving the codebase over to the drone. Even though the git won’t look a whole lot different and not a lot of actual code changed, getting these configs finalized (especially in STM32CubeMX) in was a huge step forward for the project. I expect to commit the next version of the code either today or tomorrow, with improvements specifically in TelemetryTX, BlackBoxLogging, and a few other minor changes!
1. Reworking the DMA Flow for Speed So, a lot has been going on lately, starting with a massive rework of the entire DMA flow. The functions are now entirely populated by circular DMA, which is a huge upgrade for the system’s overall performance. This architectural change allows for completely non-blocking, high-frequency calls, hitting response times as fast as 0.03 milliseconds. It basically means the whole data pipeline is significantly faster, and much more responsive than it was in previous versions. 2. Standardizing Ring Buffers and Mock Injectors To make everything more reliable, the DMA now follows a very specific and predictable pattern for both scheduled and interrupt tasks. Everything relies on ring buffers now, whether they are being injected by actual hardware (or simulated) DMA or by my custom simulation scripts. I also reworked the mock injectors so they no longer just return a pre-formatted packet. Instead, they write directly into the ring buffer, which lets me test the exact same parsing pipeline in software, simulating the hardware flow almost exactly like Renode (if only they would ever fix that USART DMA bug…). 3. Assembly BX LR Bug + STMCubeMX Clock PLL I also finally solved the long term BX LR return bug that I literally spent over 20 hours trying to find. Whenever tasks used the FPU or had slightly larger stacks, the return address in the r0 register would just get completely corrupted by the extra data. I fixed this by storing the return address in a dedicated uint value instead of blindly trusting the registers to hold it. That fix alone allowed me to safely re-add telemetryTX and make a bunch of other function logic much more robust. On top of that, I went into STM32CubeMX and reworked the clock PLL manually (it took over 3 hours to solve by hand). By feeding in a 25 MHz HSE clock, I managed to unlock the MCU’s internal clocks to run at their full 216 MHz.I also took some time to clean up the codebase to improve overall readability and commenting. 4. DMA Interrupts for Safe Parsing Another feature is that DMA now triggers interrupts when the ring buffers hit exactly halfway and when they are fully populated. This gives me the time to parse all the incoming values safely without having the risk about data getting overwritten by the next DMA pass before I am done reading it. I set up the IRQ handlers in the code to call the interrupt methods automatically, which ensures the CPU stays completely unblocked and free to handle other flight control tasks almost all the time (overall decreasing headroom). 5. Current Status: NVIC Bug Sadly, the code is actually not functional right now because the NVIC and IRQ pipeline is malfunctioning and just refusing to call the interrupts for reasons I cannot figure out yet. Because of this, I am holding off on pushing the code until I can track down the bug, give everything a final polish, and run some benchmark testing (since I don’t want to commit broken code). Also, sorry for promising a commit a day or two after my last devlog. I got super busy, especially with vacation, but I will try my best to get this sorted. Depending on how long it takes to figure out this NVIC issue, the next commit might drop anywhere from tomorrow to a few weeks from now.
Complete RTOS Overhaul, Circular DMA Pipelines, and CPU Abort Fixes This update focuses on fine-tuning the RTOS, organizing the scheduler, and rewriting microcontroller memory handling. Renode’s simulated DMA restrictions make software-only pipeline testing nearly impossible, so I plan to buy a physical dev board and Raspberry Pi to validate on silicon. While the Git diff might not look massive, the architecture has evolved from a blocking system to a true preemptive RTOS with precise polynomial timing. 1. Reworking DMA Flow & Standardizing Ring Buffers Sensor and receiver data pipelines are now driven by circular DMA, enabling non-blocking, high-frequency transfers with response times as fast as 0.3 ms (without printToUSART enabled). Everything now relies on ring buffers. Instead of generating complete packets instantly, mock injectors stream bytes directly into the buffers while updating the simulated NDTR register to mirror hardware behavior. if ( byteIdx >= frameLen ) { byteIdx = 0 ; } crsfRingBuffer [ head ] = frameBuf [ byteIdx ++ ]; head = ( head + 1 ) % CRSF_RING_BUFFER_SIZE ; currentBoardConfig . crsf_dma_stream -> NDTR = CRSF_RING_BUFFER_SIZE - head ; Feeding bytes incrementally moves the write head gradually, ensuring the simulation mimics a physical UART and allowing population functions to safely pull data within interrupt locks. 2. DMA Interrupts for Safe Parsing To prevent data overwrites, DMA streams trigger interrupts at the halfway (HTIE) and full-transfer (TCIE) points to queue tasks. Because registers like LISR are read-only in hardware, I implemented a software mock flag system (mock_LISR) for simulation: extern "C" void DMA1_Stream0_IRQHandler ( void ) { #ifdef SIMULATION uint32_t flags = mock_LISR ; mock_LISR = 0 ; #else uint32_t flags = DMA1 -> LISR ; #endif if ( flags & DMA_LISR_HTIF0 ) { DMA1 -> LIFCR = DMA_LIFCR_CHTIF0 ; decideNextInterruptTask ( & taskControlBlocks [ 1 ]); } } Functions safely extract data with interrupts disabled, ensuring the CPU never blocks waiting for peripheral transfers. 3. Assembly Bugs & CPU Aborts Debugging nested hardware interrupts exposed massive faults: The context-switching bug: FPU usage or large stack frames corrupted the return address, causing the CPU to execute BX LR with garbage data. Fixed in PendSV_Handler.S by explicitly storing excReturn inside the TaskControlBlock rather than relying on stacked registers. Second, a 0x00000000 Null Return Crash: Scheduled tasks attempted to return natively, popping 0x00000000 from the zero-initialized Link Register and causing a fetch fault. Every task now runs inside an infinite while(true) loop calling yieldCurrentTask() . 4. Clock PLLs & Hardware Re-routing After configuring the STM32CubeMX PLL for a 25 MHz HSE clock, the MCU runs at its full 216 MHz. I added a standardized initSystemGPIOClocks() function to enable peripheral clocks consistently. I also moved motor 3 from TIM1 to TIM4, synchronizing it with the 216MHz clock and freeing up a TIM1 DMA stream exclusively for the upcoming BlackBox logging. Future Updates With the core RTOS stabilized, development shifts to flight-facing systems: hooking the SPI pipeline to the video overlay for live OSD goggle telemetry, adding permanent flash storage for PID tunes/configs, exposing MCU controls via CLI, supporting programmable transmitter bind buttons, building a desktop GUI, and using the freed TIM1 DMA for BlackBox logging. Below, I have attached a screenshot of every single task working, and outputting values in the simulation, something that wasn’t possible for the interrupt tasks until now.
1. CLI Upgrades and Channel Mapping Over the past 5 days, I knocked out some major quality-of-life upgrades for the flight controller. I added customizable channel mapping for CRSF directly through the CLI. By utilizing a combination of references and pointers, I was able to point a volatile alias to certain channel mappings, allowing for completely customizable CRSF channels. While I was in the CLI logic, I also added the ability to configure custom IMU orientations around the z-axis, making it way easier to mount the board in different directions without having to recompile the firmware. 2. DMA Pipeline Fixes and Baro Optimization A big chunk of this session went into debugging and refactoring the hardware data pipelines. I adjusted the ADC population pipeline in order to fix some stream parsing issues, and gave the barometer a massive overhaul. The baro now uses a non-blocking, non-circular DMA setup that relies on a semi-interrupt-driven task call rather than a continuous loop. This enabled me to yield in the middle of the function, and call back later as soon as DMA is finished transferring. I also fixed a massive bug in the DMA setup: the DMA_SxCR_HTIE and DMA_SxCR_TCIE flags were accidentally enabled for the GPS and OSD ring buffers, which would have crashed the whole RTOS by firing unhandled interrupts, causing default HAL and IRQ handlers to run. 3. Walksnail Two-Way Communication Finally, I built out the respondToVTX task to get the drone properly communicating with the Walksnail VTX. Instead of just blindly sending video data out, this sets up true two-way communication over asynchronous UART. By writing a persistent byte-by-byte state machine, the flight controller can now safely parse incoming requests from the VTX without dropping half-packets, allowing me to send back live stick positions and armed status for the OSD menus. The only thing left to do now is handle persistent CLI storage, the logToBlackBox task, integrating the semi-interrupt pipeline with telemetryTX and the OSD, and a few other system tweaks. After that the RTOS will be 100% ready to fly the actual drone.
1. Scheduler Hardening and Task Discovery Spent part of this session making the RTOS scheduler itself more robust. I added a findTaskByName() function so interrupt-driven task lookups happen dynamically instead of relying on hardcoded task indices, which was a fragile setup just waiting to break the moment task registration order changed. Alongside that, I wrapped every one of those lookups with lambdas that throw a proper warning if a task can’t be found, so a missing task fails loudly instead of silently dereferencing a null pointer somewhere downstream. 2. Blocking vs Non-Blocking Scheduled Tasks After a lot of painful debugging, I traced a string of issues (including interrupt tasks silently failing to fire and scheduled tasks stepping on each other) back to non-blocking statements I’d introduced inside scheduled tasks in the previous devlog. I ended up taking those out and reverting scheduled tasks fully back to blocking, which is genuinely what the scheduled zone was designed for in the first place: tasks that don’t need to run in polynomial time and can safely block without putting the drone at risk of crashing. Once that was sorted, I converted the raw USART byte-pushing into a blocking DMA system instead, so streaming is handled by DMA hardware rather than manually pushed byte-by-byte over USART. 3. Flight Loop Refactor and VTX Communication On the flight logic side, I refactored flightLoop() to use lambdas and a state machine so I would never have to yield in the middle of my tasks (which was used previousely for zeroing motor outputs). I also converted the error handling into a proper state machine, so different types of warnings can be caught and handled distinctly instead of lumping every issue into one path, and turned on full debug printing whenever the system is running. Last but not least, I built out a dedicated injector for respondToVTX so I can properly test and simulate VTX communication going forward. 4. Real Hardware Testing Since, I am almost 100% done with the RTOS I bought two NUCLEO-F756ZG’s which will serve as testing the 100% production code rather than the simulation version. I bought two so that one of the boards will be running the real RTOS code, testing DMA reading from USART, SPI, and emitting real motor signals, while the second will be emulating all of my peripherals. When the boards come, if the code does work, it will also mean that the real PCB’s will also work when my friend finishes with them. 5. Next Steps As of now there are a few issues with the double preempt system (for the interrupt tasks) I designed a while back, so I will need to debug that before they arrive. Right now before the boards arrive, I will be finishing up the RTOS blocking DMA pipelines (stripping out IRQ, removing NVIC etc. for scheduled tasks), and performing a bunch of baseline testing in order to prove that the RTOS is capable of flying an actual drone!
For the past couple weeks I have been mostly refining small bugs, which were originally rendering the RTOS usless, as well as refactoring the code for simplicity rather than elegancy. 1. Transition to Actual Hardware Renode is great for simulation, but it ended up hiding a bunch of race conditions in the population methods and the main scheduler that led to no code actually running during the IRQ handler calls. I spent a lot of time securing those resources and finally got the scheduler mostly finished up. Also, I decided to just take out the double preempts entirely since they were adding way too much complexity for absolutely zero speed benefits in practice (plus they were creating detrimental but rare race conditions). 2. Code Refactor To make things run a bit more consistent, I restructured how the control loops operate. I ended up merging the dShotGeneration task with the parent IMU control loop, so now everything from reading the sensors to pushing motor commands happens in one clean sequence (rather than including an unnecessary preempt). I also built out a brand new CRSF control loop to handle all the radio parsing and state machine updates in its own dedicated space (just for consistency). Lastly, while doing a final pass on the code, I realized that in my dShotGeneration helper, I had forgot to scale the values by a factor of 1/2000, as the inputs require the value to be between 0 and 1 (not 0 and 2000). 3. Final DMA Iteration The biggest relief was finally getting rid of the old blocking USART system. I finally transitioned away from manually handling the streaming by polling flags in a loop, which was tying up the processor way more than it should have. Everything has been converted over to a blocking DMA system, so the hardware can just handle all the memory transfers in the background while the CPU focuses on actually keeping the drone in the air. Overall, I think that this will be the final iteration of the RTOS DMA pipeline, as now that it is solid and works with all the edge cases, theres no need to change it. 4. Timeline With almost all the major bugs fixed, I am going to ship the project! While it is not 100% done, it will likely be as close as it will get during the timeframe of the Stardance hackathon, and with it so close, theres no reason not to ship. Anyway, I will keep the source as the main project rather than a release, so if I do end up fixing any major bugs, they will get patched either before or during project review (besides most of these bugs are only detectable on actual hardware). I will most likely finish the RTOS in the next couple weeks (by the end of October), and will finish the drivers for the ESC and RM by latest end of November (for more timing refer to the README). Thanks to everyone who supported me throughout this project (hopefully me and my friend can get this drone flying in another Hack Club Hackathon!).
Although I haven’t been able to make much progress on the project due to school over the past weeks I did manage to make some substantial project mostly with the scheduler, allowing for fairly good performance on my STM Nucleo boards, rather than it just crashing instantly. 1. Issues with the Old Design The flight controller’s RTOS scheduler wasn’t working under real hardware timing because global variables like currTaskIndex, topOfStack, and excReturn were being modified from multiple entry points simultaneously. Yield calls, interrupt handlers, and the main task-selection logic were all making independent writes to the same shared task control blocks. Whenever a hardware interrupt fired mid-write, stack pointers and exception return values were saved into the wrong taskControlBlocks slot. This created edge cases where preempted tasks were silently double-queued, state tracking completely desynchronized, and time-critical hardware events were dropped during flight. 2. Centralizing Context Switches in PendSV To eliminate these race conditions, all state-selection decisions were moved out of individual execution contexts and consolidated into decideNextTask(), which now runs exclusively inside the PendSV assembly handler. Functions like yieldCurrentTask(), decideNextInterruptTask(), and decideNextScheduledTask() no longer touch global scheduler state or choose which task runs next; they simply update local status flags like TaskState::BLOCKED or TaskState::READY inside brief __disable_irq() critical sections and trigger PendSV using SCB_ICSR_PENDSVSET_Msk. When PendSV fires, decideNextTask() saves outgoingSP and outgoingExcReturn to the active block, sets RUNNING tasks to READY, and runs a two-pass check over taskControlBlocks[]—evaluating high-priority TaskType::INTERRUPT tasks first, then TaskType::SCHEDULED tasks, and falling back to index 0 for idle. Since PendSV is the only place allowed to modify currTaskIndex, concurrent state corruption is structurally impossible.
Ships:
Phase 1 (The Custom RTOS Kernel) I am building a custom FPV drone from scratch with my friend in an attempt to achieve 200 mph . At those speeds, standard super loops are far too slow, as a single microsecond of lag means the drone travels feet before making a PID correction. To solve this, I built a custom, hybrid cooperative/preemptive Real-Time Operating System (RTOS) from scratch for the ARM Cortex-M7 (STM32F7)! What Was Challenging? The absolute hardest part of this phase was writing the low-level assembly context switcher ( PendSV_Handler.S ). Hacking the ARM Cortex-M7 registers to physically hot-swap stack pointers ( PSP ) mid-execution without crashing the CPU or corrupting the program state was a massive headache. I had to ensure that when an asynchronous preemption event fires, the assembly code perfectly preserves the execution registers ( R4-R11 plus the hardware-stacked registers), jumps to the high-priority task, and then seamlessly restores the background task right where it left off. Getting the compiler-enforced task overloading working so the kernel cleanly separates periodic tasks from raw interrupts was another huge hurdle, but it makes the whole codebase infinitely more robust. Want to Test It or Write Your Own Tasks? You don’t need physical hardware to watch this run! I’ve packaged the entire project with a Renode simulation script. You can run the interactive emulator with a single command to see the preemption scheduler hijack our counter task live, or you can jump into the user sandbox file and write your own custom tasks. Check out the GitHub Repository to Run the Demo! The README has a quick 5-minute setup guide to get you compiling and simulating your own code in no time.
200 MPH Drone RTOS: How it went Simulation to Silicon! When I published the first major update for this project at the 15-hour mark, I essentially had a proof-of-concept. I had managed to write a custom hybrid cooperative/preemptive scheduler and get an ARM Cortex-M7 (on the STM32f756VGTx) assembly context switcher functioning inside the Renode simulator. Although this looked like a massive leap, the project had barely started. Over 100 hours of development have gone into this project since then. The transition from a simulated kernel to a bare-metal flight controller capable of keeping a 200 MPH drone in the air required stripping the system down to its bare components, rewriting the memory architecture, and debugging some of the most frustrating hardware faults I have ever encountered-especially whenever the project just crashed, leaving me no traces as to what went wrong. Software Limitations The biggest turning point was realizing that software simulation can only take you so far. Renode is an amazing tool, but it hid a massive amount of race conditions and timing flaws that only exposed themselves when I finally flashed the code to physical STM32F756 Nucleo board. Sometimes, the MCU would have to reboot every 2 seconds. I spent dozens of hours tracking down massive architectural bugs. One of the worst was a context-switching fault where floating-point math was corrupting the return address in the CPU registers, causing the processor to execute raw garbage data. To fix this, I had to completely restructure the boot sequence and overhaul the memory management. I moved away from static arrays to a dynamic per-task stack allocation system, and implemented a dual-guard memory system. Arguably, this was the most important change as it allowed me to debug tasks way quicker as I instantly could know when the canary values were corrupted. Overhauling the Data Pipelines In order to avoid the 10ms delay of my original blocking pipline (for data transfer), I entirely rewrote the hardware data pipelines multiple times to use a semi-blocking, circular Direct Memory Access (DMA) architecture. Instead of the processor handling the data transfer, the hardware itself moves the bytes from the sensors directly into memory in the background. The DMA streams now trigger precise interrupts at the halfway and full-transfer points, allowing the RTOS to safely parse incoming radio packets and sensor reads without ever risking data overwrites or blocking the main flight loop. Building a Flight Controller I integrated Betaflight’s Actual Rates algorithm with custom expo curves, ensuring that physical stick inputs map perfectly to target velocities. I built a custom SPI driver to pull 24-bit raw pressure and temperature data from a BMP390 barometer, passing it through hypsometric formulas to calculate altitude. For video and telemetry, I moved away from raw UART strings and implemented native MSP DisplayPort communication (for true digital Walksnail Avatar connection). I also built an interactive USB command-line interface. Instead of having to recompile and re-flash the firmware every time a PID value needs to be adjusted, I can plug into the board, pull up a terminal, and manually tune gains, remap CRSF radio channels, or adjust IMU orientation offsets in real time. Final Version The last few weeks have been about refinement and consistency, removing race conditions and overall polishing the code. Now that the RTOS is fully functional, the only things to add are extra features, like a permanent + more expanded CLI, as well as finishing the external RM and linking the ESC drivers. As these are pretty insignificant, I have linked the source of this shipment to the Github project rather than a specific release so that I can update these during review!...
|
1792842857.8059814
|
1792929257.8059814
|
|
25672
|
NeoOS by @WilGulf | Stardance
|
WilGulf
|
1
|
130.63
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-07T16:36:40Z", "hours": 118.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "So I made my own os “NeoOS”, this project was started not long after the release of the macbook neo and cause of that the os got it’s name. Testing is pretty easy, just make sure you have qemu installed with the correct cpu architecture. The project has become much bigger than I thought when I started it, I just could not stop implementing more features. And the most challenging part must be the hardware interaction, like reading from a disk by sending commands in hex. The documentation found for how cpus/ata disks/ram and all works are so hard to understand but that may be because it is not a simple thing. So when testing I want you to know that even though there aren’t many commands the kernel is still doing so much work not seen and the kernel is also what took most hours on this project. Lastly I hope that someone with all documentation provided writes their own program for NeoOS, I would love to see what other people could do with my OS.", "repo": "https://github.com/WilGulf/NeoOS", "demo": "https://github.com/WilGulf/NeoOS/releases/tag/1.1.0"}]...
|
[{"post_id": 74057, "ts": "202 [{"post_id": 74057, "ts": "2026-09-26T16:34:52Z", "hours": 9.7725, "text": "Devblog 22 The review of NeoOS found some problems with the filesystem commands and I took some time to fix them. Now the commands know where they are run and you can move around with cd. The second thing for this devblog is that I got multi disk support so now you can have up to 4 harddrives attached with writing/reading working on all of them , and that took some interractions with the ata ports but I finally got it working. –WilGulf"}, {"post_id": 69740, "ts": "2026-09-17T18:05:34Z", "hours": 2.8608, "text": "Devblog 21 The review is taking ages, and I couldn’t leave NeoOS. So I of course continued with fixes and features. The big new thing in this devblog is the filesystem: NeoFS, I have done major changes to the design like how a free block is found which greatly improves performance. And many safeguards were added to reading/writing to the filesystem. I also updated the host fstools used to create the filesystem on a drive to support arguments with options, easier to use now. – WilGulf"}, {"post_id": 63661, "ts": "2026-09-07T16:18:42Z", "hours": 1.5858, "text": "Devblog 20 Final touches and logging the remaining time before shipping. I hope I have fixed all small bugs and made the libraries easy to use in new C programs. I also created some small new programs to show all functions in the kernel. Thats all for now and I hope to get to ship soon. –WilGulf"}, {"post_id": 63163, "ts": "2026-09-06T20:16:36Z", "hours": 1.6142, "text": "Devblog 19 Not as big of a update now, but I just wanted to show of my new readdir implementation. That is then used by list with some coloring, all are green cause there is only executables in /execs. – WilGulf"}, {"post_id": 63079, "ts": "2026-09-06T17:50:19Z", "hours": 9.9825, "text": "Devblog 18 So close to going over 10h however I managed to fix the bug. So a missing feature has been writing to the disk and not only read it. But the current file system was fat16 and the attempts I have made to implement writing has failed. What is the logical thing to do in this scenario, well I wrote my own filesystem: NeoFS. It is not feature rich compared to fat16 but that was why I could make it write. And the performance may be horibble when having larger files. But if it works I will keep it! So the test just shows writing/reading of a file. – WilGulf"}, {"post_id": 60414, "ts": "2026-09-02T18:32:35Z", "hours": 3.4125, "text": "Devblog 17 I have developed my own ramfs. The old filesystem only supported fat16 with only reading capabilities which maded it feel very limited with what you could do with it. So the first step I dicided to do with adding reading was to implement filesystem that lives in ram which is really easy to write to. Now the D: drive is used for the ramfs (D as in dev used by linux). In the screenshot test.elf creates a file: D:/test.txt and writes HELLO FROM RAMFS to it. Then read.elf (linux cat) can read that file. – WilGulf"}, {"post_id": 58085, "ts": "2026-08-30T13:12:46Z", "hours": 7.1003, "text": "Devblog 16 I wanted more programs for NeoOS but I felt really limited to just the print and getkey functions. So I rewrite ncurses (not with all ncurses functions) into my own curses for NeoOS. This allowed me to write programs that update the terminal like a screen. Here I shouw the NeoOS logo being displayed with a CRT-screen effect. Now I am In the decision of what game to make with this new library. – WilGulf"}, {"post_id": 56739, "ts": "2026-08-28T16:47:26Z", "hours": 4.86, "text": "Devblog 15 I have now added colored printing that I showcase here in the fetch program. The printing made me redo how the writer works to support the decoding of the default ascii sequence used to change color: \\033[xxm , where xx is the color. It follows the standard C way which makes it pretty easy to use colored printing. – WilGulf"}, {"post_id": 53943, "ts": "2026-08-24T17:27:07Z", "hours": 13.1097, "text": "Devblog 14 I have worked on multiple commands for the shell but the one that i want to showcase now is the process management. Running.elf lists all running processes (including itself) with their name, pid and privilege level. Then I execute kill.elf with “1” as a argument which is the id of the shell. That kills the shell which leaves the os with just the blinking underscore cause there is no longer a shell running. Other commands are the sysinfo (uname) and updating fetch.elf with some new info."}, {"post_id": 49143, "ts": "2026-08-17T19:33:20Z", "hours": 9.0397, "text": "Devblog 13 Not much to show on the screen, however in the background i have worked on a security feature. So it is kind of like the pledge feature in open-bsd: so the kernel first need to know from the program what it wants to do, like print to the screen. Not until it has told what it will do it won’t be able to do anything. And if the program ever does anything it hasn’t told it would do the kernel will just kill the process. The program promise to not use any other functions except the one it has specified. And the kernel won’t let it break it’s promise. That is why the c function is called promise. –WilGulf"}, {"post_id": 48037, "ts": "2026-08-16T10:16:51Z", "hours": 9.1672, "text": "Devblog 12 A shell This is the first shell for NeoOS, it only has the clear command and a fetch program to display the logo. It took some time implementing all syscalls it needed, i needed to make many kernel functions available in the userprogram and that took most my time. I have yet to implement a way to terminate the program so now when fetch is called it never ends and you never get back to the shell again. – WilGulf"}, {"post_id": 46769, "ts": "2026-08-14T08:19:44Z", "hours": 4.0228, "text": "Devblog 11 NeoOS has now got ps2 keyboard drivers! So now I have implemented a driver for the US layout on ps2 keyboards. I am also looking into changing it to support my keyboards (Swedish layout), but that would take some time to map the right keys to the right number when the US layout can be found right on the os dev wiki. So this keyboard writing is also done the real way, the kernel boots, the kernel starts a program, that program asks for keyboard input, when it gets a input it asks the kernel to print that input. Now you can see how this could later change into a shell: you write the name of a command and it runs it. –WilGulf"}, {"post_id": 44096, "ts": "2026-08-09T17:04:23Z", "hours": 7.8719, "text": "Devblog 10 Now a binary file is able to be loaded AND executed. So from that qemu picture you can see the “Hello from a binary program” message, that message is printed by that print.s code. This is a huge milestone. There are still some functions to implement. But after that it is just writing all the userland programs. I must say none of this was easy, and choosing to develop a OS for stardance has so far been a really big challange. And now I need to push through coding the last bit so i can get this shipped before the end of stardance. –WilGulf"}, {"post_id": 43351, "ts": "2026-08-08T15:42:07Z", "hours": 7.6778, "text": "Devblog 9 I found a course for os development to follow and it really helped me with this. With the course explaining, I got my kernel to read a fat16 disk and print the content of hello.txt. This was a lot of debugging, first the file did open but couldn’t be read, then later the file was read but the data was just wierd characters. Now this is a huge step, the reading is done and now I only need it to be able to understand executables and read them. – WilGulf"}, {"post_id": 42494, "ts": "2026-08-07T08:16:16Z", "hours": 3.1131, "text": "Devblog 8 The real malloc and free is implemented. And if you know C you may understand what is going on in the image. All ptr are allocated using malloc and the first one is free’d before allocating ptr4, that is why their values are the same: ptr4 reuses ptr1 because ptr1 is free’d. Now that I don’t only have the allocation but also freeing which means I can safely implement a filesystem driver without worrying about the memory overflowing. – WilGulf"}, {"post_id": 42122, "ts": "2026-08-06T18:15:14Z", "hours": 4.8756, "text": "Devblog 7 I got to usermode, there I could run all the programs like a shell. But when jumping to usermode you need somewhere to land, some address where the cpu can start executing code. I don’t have that. So the next thing would be to implement a filesystem driver to read a disk and execute an executable. But my memory management is to simple to do it in a good way, I can only allocate and never delocate. So now I am going to revamp my memory management. –WilGulf"}, {"post_id": 26450, "ts": "2026-07-11T08:03:23Z", "hours": 11.1347, "text": "10h I don’t have anything to show right now, but as you can see i have 11 more hours on this project. I have both been working on fixes and some new things needed for the next big thing being: getting to ring 3/usermode. –WilGulf"}, {"post_id": 24029, "ts": "2026-07-07T09:45:52Z", "hours": 4.8494, "text": "Now we got progress! The kernel now has my implementation of paging working. If you don’t know what paging is, it is basically a way of managing the memory that is used by most x86 operating systems. The paging implementation took some C files and some updates to the assembly files but now it is done! Now I have many ways to go with what to implement next. We’ll see what i choose. –WilGulf"}, {"post_id": 19092, "ts": "2026-06-30T09:33:07Z", "hours": 7.1717, "text": "IDT Done! Now I got the IDT working. You can see it is working cause when that “Divide By Zero” message appears the kernel does a division with a zero. Normally that would make the kernel start bootlooping but now the interrupt handler handles the divide by zero and stops the kernel instead. Interrupt handling does not only stop crashes but also allow keyboards or syscalls to invoke interrupts that the kernel can process. Next step may be to use that interrupt handler to implement syscalls. – WilGulf"}, {"post_id": 18225, "ts": "2026-06-29T09:29:17Z", "hours": 2.5817, "text": "Hello Yes we got it, hello world on the screen. Turns out the loading of the kernel set up the stack wrong. One line of assembly was in the wrong section. I also continued with the GDT. It isnt something you can see but in the background the GDT has been initialized. That is my progress so far, now after GDT comes some interrupt handling. –WilGulf"}, {"post_id": 17849, "ts": "2026-06-28T19:51:48Z", "hours": 3.3697, "text": "HELP What is wrong? Well as I said, I would continue with getting Hello World on the screen. Well that wasn’t hard to do the “simple” way, just pasting several lines for each letter. But I wanted to do it the real way, implement a writer function. I started with making a function to clear the framebuffer from that “Booting…” message. And also moving the cursor to the start. Then I continued with the writer. The writer took two parameters: (char *buf, unsigned int len). Both arguments needed. The function i began testing with calling it from kmain like this: writer(“hello”, 5) . Then in the function i processed it like this: unsigned char *bytes = (unsigned char *)buf; for (unsigned int i = 0; i < len; i++) { fb_write_cell(pos * 2, bytes[i], 0x0F, 0x00); pos++; } But thats where it went wrong. When compiling and booting after adding that function content it wouldn’t boot. And that is wierd cause when i removed the bytes[i] line it booted. Somehow my kernel cant handle it. I don’t know what could currently be causing this. What now? Well i will of course try to solve this myself or find a way to make the writer work. And if you have any knowledge in C and os development please help me. The projects current code can be found on my github. – WilGulf"}, {"post_id": 15860, "ts": "2026-06-25T19:46:32Z", "hours": 1.8089, "text": "I got my OS booting without entering a bootloop!!! I wanted to leave assembly and start to use C. But to do that I need to call the main function in C from assembly. Much googling and testing later I got it to actually use the main functions while loop to keep itself from bootlooping. Never thought i would be so happy about completing the first and easiest task in os development. I will comeback with another post when i get ‘hello world’ on the screen."}]...
|
Title: NeoOS by @WilGulf | Stardance
Devlogs:
I g Title: NeoOS by @WilGulf | Stardance
Devlogs:
I got my OS booting without entering a bootloop!!! I wanted to leave assembly and start to use C. But to do that I need to call the main function in C from assembly. Much googling and testing later I got it to actually use the main functions while loop to keep itself from bootlooping. Never thought i would be so happy about completing the first and easiest task in os development. I will comeback with another post when i get ‘hello world’ on the screen.
HELP What is wrong? Well as I said, I would continue with getting Hello World on the screen. Well that wasn’t hard to do the “simple” way, just pasting several lines for each letter. But I wanted to do it the real way, implement a writer function. I started with making a function to clear the framebuffer from that “Booting…” message. And also moving the cursor to the start. Then I continued with the writer. The writer took two parameters: (char *buf, unsigned int len). Both arguments needed. The function i began testing with calling it from kmain like this: writer(“hello”, 5) . Then in the function i processed it like this: unsigned char *bytes = (unsigned char *)buf; for (unsigned int i = 0; i < len; i++) { fb_write_cell(pos * 2, bytes[i], 0x0F, 0x00); pos++; } But thats where it went wrong. When compiling and booting after adding that function content it wouldn’t boot. And that is wierd cause when i removed the bytes[i] line it booted. Somehow my kernel cant handle it. I don’t know what could currently be causing this. What now? Well i will of course try to solve this myself or find a way to make the writer work. And if you have any knowledge in C and os development please help me. The projects current code can be found on my github. – WilGulf
Hello Yes we got it, hello world on the screen. Turns out the loading of the kernel set up the stack wrong. One line of assembly was in the wrong section. I also continued with the GDT. It isnt something you can see but in the background the GDT has been initialized. That is my progress so far, now after GDT comes some interrupt handling. –WilGulf
IDT Done! Now I got the IDT working. You can see it is working cause when that “Divide By Zero” message appears the kernel does a division with a zero. Normally that would make the kernel start bootlooping but now the interrupt handler handles the divide by zero and stops the kernel instead. Interrupt handling does not only stop crashes but also allow keyboards or syscalls to invoke interrupts that the kernel can process. Next step may be to use that interrupt handler to implement syscalls. – WilGulf
Now we got progress! The kernel now has my implementation of paging working. If you don’t know what paging is, it is basically a way of managing the memory that is used by most x86 operating systems. The paging implementation took some C files and some updates to the assembly files but now it is done! Now I have many ways to go with what to implement next. We’ll see what i choose. –WilGulf
10h I don’t have anything to show right now, but as you can see i have 11 more hours on this project. I have both been working on fixes and some new things needed for the next big thing being: getting to ring 3/usermode. –WilGulf
Devblog 7 I got to usermode, there I could run all the programs like a shell. But when jumping to usermode you need somewhere to land, some address where the cpu can start executing code. I don’t have that. So the next thing would be to implement a filesystem driver to read a disk and execute an executable. But my memory management is to simple to do it in a good way, I can only allocate and never delocate. So now I am going to revamp my memory management. –WilGulf
Devblog 8 The real malloc and free is implemented. And if you know C you may understand what is going on in the image. All ptr are allocated using malloc and the first one is free’d before allocating ptr4, that is why their values are the same: ptr4 reuses ptr1 because ptr1 is free’d. Now that I don’t only have the allocation but also freeing which means I can safely implement a filesystem driver without worrying about the memory overflowing. – WilGulf
Devblog 9 I found a course for os development to follow and it really helped me with this. With the course explaining, I got my kernel to read a fat16 disk and print the content of hello.txt. This was a lot of debugging, first the file did open but couldn’t be read, then later the file was read but the data was just wierd characters. Now this is a huge step, the reading is done and now I only need it to be able to understand executables and read them. – WilGulf
Devblog 10 Now a binary file is able to be loaded AND executed. So from that qemu picture you can see the “Hello from a binary program” message, that message is printed by that print.s code. This is a huge milestone. There are still some functions to implement. But after that it is just writing all the userland programs. I must say none of this was easy, and choosing to develop a OS for stardance has so far been a really big challange. And now I need to push through coding the last bit so i can get this shipped before the end of stardance. –WilGulf
Devblog 11 NeoOS has now got ps2 keyboard drivers! So now I have implemented a driver for the US layout on ps2 keyboards. I am also looking into changing it to support my keyboards (Swedish layout), but that would take some time to map the right keys to the right number when the US layout can be found right on the os dev wiki. So this keyboard writing is also done the real way, the kernel boots, the kernel starts a program, that program asks for keyboard input, when it gets a input it asks the kernel to print that input. Now you can see how this could later change into a shell: you write the name of a command and it runs it. –WilGulf
Devblog 12 A shell This is the first shell for NeoOS, it only has the clear command and a fetch program to display the logo. It took some time implementing all syscalls it needed, i needed to make many kernel functions available in the userprogram and that took most my time. I have yet to implement a way to terminate the program so now when fetch is called it never ends and you never get back to the shell again. – WilGulf
Devblog 13 Not much to show on the screen, however in the background i have worked on a security feature. So it is kind of like the pledge feature in open-bsd: so the kernel first need to know from the program what it wants to do, like print to the screen. Not until it has told what it will do it won’t be able to do anything. And if the program ever does anything it hasn’t told it would do the kernel will just kill the process. The program promise to not use any other functions except the one it has specified. And the kernel won’t let it break it’s promise. That is why the c function is called promise. –WilGulf
Devblog 14 I have worked on multiple commands for the shell but the one that i want to showcase now is the process management. Running.elf lists all running processes (including itself) with their name, pid and privilege level. Then I execute kill.elf with “1” as a argument which is the id of the shell. That kills the shell which leaves the os with just the blinking underscore cause there is no longer a shell running. Other commands are the sysinfo (uname) and updating fetch.elf with some new info.
Devblog 15 I have now added colored printing that I showcase here in the fetch program. The printing made me redo how the writer works to support the decoding of the default ascii sequence used to change color: \033[xxm , where xx is the color. It follows the standard C way which makes it pretty easy to use colored printing. – WilGulf
Devblog 16 I wanted more programs for NeoOS but I felt really limited to just the print and getkey functions. So I rewrite ncurses (not with all ncurses functions) into my own curses for NeoOS. This allowed me to write programs that update the terminal like a screen. Here I shouw the NeoOS logo being displayed with a CRT-screen effect. Now I am In the decision of what game to make with this new library. – WilGulf
Devblog 17 I have developed my own ramfs. The old filesystem only supported fat16 with only reading capabilities which maded it feel very limited with what you could do with it. So the first step I dicided to do with adding reading was to implement filesystem that lives in ram which is really easy to write to. Now the D: drive is used for the ramfs (D as in dev used by linux). In the screenshot test.elf creates a file: D:/test.txt and writes HELLO FROM RAMFS to it. Then read.elf (linux cat) can read that file. – WilGulf
Devblog 18 So close to going over 10h however I managed to fix the bug. So a missing feature has been writing to the disk and not only read it. But the current file system was fat16 and the attempts I have made to implement writing has failed. What is the logical thing to do in this scenario, well I wrote my own filesystem: NeoFS. It is not feature rich compared to fat16 but that was why I could make it write. And the performance may be horibble when having larger files. But if it works I will keep it! So the test just shows writing/reading of a file. – WilGulf
Devblog 19 Not as big of a update now, but I just wanted to show of my new readdir implementation. That is then used by list with some coloring, all are green cause there is only executables in /execs. – WilGulf
Devblog 20 Final touches and logging the remaining time before shipping. I hope I have fixed all small bugs and made the libraries easy to use in new C programs. I also created some small new programs to show all functions in the kernel. Thats all for now and I hope to get to ship soon. –WilGulf
Devblog 21 The review is taking ages, and I couldn’t leave NeoOS. So I of course continued with fixes and features. The big new thing in this devblog is the filesystem: NeoFS, I have done major changes to the design like how a free block is found which greatly improves performance. And many safeguards were added to reading/writing to the filesystem. I also updated the host fstools used to create the filesystem on a drive to support arguments with options, easier to use now. – WilGulf
Devblog 22 The review of NeoOS found some problems with the filesystem commands and I took some time to fix them. Now the commands know where they are run and you can move around with cd. The second thing for this devblog is that I got multi disk support so now you can have up to 4 harddrives attached with writing/reading working on all of them , and that took some interractions with the ata ports but I finally got it working. –WilGulf
Ships:
So I made my own os “NeoOS”, this project was started not long after the release of the macbook neo and cause of that the os got it’s name. Testing is pretty easy, just make sure you have qemu installed with the correct cpu architecture. The project has become much bigger than I thought when I started it, I just could not stop implementing more features. And the most challenging part must be the hardware interaction, like reading from a disk by sending commands in hex. The documentation found for how cpus/ata disks/ram and all works are so hard to understand but that may be because it is not a simple thing. So when testing I want you to know that even though there aren’t many commands the kernel is still doing so much work not seen and the kernel is also what took most hours on this project. Lastly I hope that someone with all documentation provided writes their own program for NeoOS, I would love to see what other people could do with my OS....
|
1792853508.8577504
|
1792939908.8577504
|
|
950
|
Volvo API playground by @Kl | Stardance
|
Kl
|
1
|
130.69
|
2165
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-06T20:19:02Z", "hours": 113.0, "multiplier": 19.09, "payout": 2165, "blessing": "neutral", "text": "A Milestone Welcome everybody to the first ship of this project. What is this project? This project simulates how the Volvo API works. Why was this project created in the first place? So it’s easier to create and test your app that uses the Volvo API without using ready-made libraries. I can’t find the features you mentioned in the devlog? Check the scope branch or the upcoming dev branch. What I’m proud of: I learned a lot of things (this is my first big public project): Tailwind Python stuff (I started with C++) Writing documentation (Markdown) I also learned the basics of OAuth2 and how APIs work. Making a project with all the features needed to get started. I created an additional document for you, Stardancers: Stardance PATH What is still on the TODO list? More APIs Scopes docker image See the roadmap . What to see? error logger recreated endpoints dashboard web socket realtime changes internal endpints interactivity of the car", "repo": "https://github.com/klswar0/Volvo-API-playground", "demo": "https://playground.kls.hackclub.app/"}]...
|
[{"post_id": 79861, "ts": "202 [{"post_id": 79861, "ts": "2026-10-15T17:17:04Z", "hours": 7.1881, "text": "Devlog A very long time no see. I have made a lot of progress on adding an SQL database. Testing on MySQL The backup server provider gives me access to one. SQL changes A lot of BIG changes. The reason why I haven’t posted for so long is that I didn’t want to post about something that doesn’t work. Created new classes to intercept data changes, both in case of direct dict changes and when going through the SQL model library. (I didn’t change much in the code, just imported it under the same name as the old database.) And it works! At the start, only reading data worked, but now it can send data back to the database. Added a regen function to make sure built-in API keys are always the same after a restart. Fixed the car creation function (but when I started storing the additional data in SQL, it broke AGAIN). Added configuration attributes. At first, just the database type, but then all the information needed to connect to a MySQL database. Fixed the scenario readconfig (wasn’t it updated in the past?). A really small number of things I added? Yes, but the amount of debugging and changing built-in functions in classes took me so long. (Could be because of school and not having time to do everything in one sitting.) But I learned how to make a dummy dict with a class, as I called it in the commit: proxy . Problems The SQL database can’t create new cars. Need to check all internal endpoints to make sure they work. Still haven’t merged the DEV branch (TODO). All information is stored in one column as JSON. It works, buuuut I don’t like it that way. Need to clean up the proxy classes. updating docs What failed this time? The second photo attached shows it. Have a look before reading more. _contains__ If you see it, congrats! But if you have a screen full of code, it’s harder to spot. It should be __contains__ , and I spent, I think, two days debugging why all in statements just didn’t work. I was so happy when I finally found it! XAMPP and Apple Silicon The manager app doesn’t open after a restart, so after consulting Google and AI, I found a command that I use: sudo /Applications/XAMPP/xamppfiles/xampp start xampp If someone is asking why I didn’t use the server provided one it just wasn’t practical for now."}, {"post_id": 73730, "ts": "2026-09-25T19:56:38Z", "hours": 4.9217, "text": "Devlog A lot of breaking changes later and getting closer to PR and going to 2 ship Token lifetime WORKS made time when token is expired is rounded to int endpoint for invalidating token possibility for enabling expiring token for a specific token when it’s disabled for all in config file misc pytest for location and energy API fix the url for the enrgy API (it started with connectivity url not wi the energy api) cleanup import created new files to make the code more readable dashboard added location values added capabilities and values auto token generation and expiration in dashboard"}, {"post_id": 69307, "ts": "2026-09-16T19:32:57Z", "hours": 5.58, "text": "Read till the end PLEASE 🥺 Devlog A lot of changes from the last devlog only 5 hrs Mostly bc I add stuff on top of but I will suffer in the future (See dev branch for these changes) Change all connectivity API to start with /connected-vehicle/v2/ to make room for different APIs After that I fixed pytest to reflect that I added a location API with a ONE endpoint There were few iterations Added scopes for this API location:read If I remember correctly Unique error responses (because location error look like old (v1) connectivity API Fixed that operationID could be a constant Added a config option to use longer API keys About here I Fixed the url for location api because it used the connectivity scheme Started working on energy API I added all data from it to this project In short one endpoints says what this car is capable of And the other says the values TODO: add its unique error its half done Scopes corresponding to energy API Fixed that some values and capabilities had the same name (bc of that some bool values where number so a it was important) WIP Oauth2 update: JWT tokens for access tokens WIP token can expire! what are JWT token? There consist of three parts header payload and signature The two first is important for my project the 3 isn’t . They store info about issuer, when token is active, scopes… A lot of useless data for this project or useful? We will see Life Bc of school I didn’t have a lot of time to write devlog (and write code) but I try to do my best before the end of stardance The next ship is planned for 28 September BIRTHDAY TODAY (16 September) Im celebrating my birthday 😄 If you write me a few good words It would be motivating for me."}, {"post_id": 63130, "ts": "2026-09-06T19:20:58Z", "hours": 1.6042, "text": "Devlog MAIN added a startup baner fixed snapshots not saving if website was used scope If you didn’t follow from start this branch contains the experimental scope support which is mostly done but I can guarantee stability that’s why it isn’t pushed to main in fist ship made the scope page work added a back button added button to scope on main page fixed snapshots loading/saving"}, {"post_id": 61052, "ts": "2026-09-03T19:22:00Z", "hours": 2.8836, "text": "Long time no see… The school started aaand I didn’t have time I had low motivation The only thing to do before first ship is docs but I could find time to finish them. Buut I back now. AND I promise to ship it before 7 of September. If you are voting on this project PLEASE do not skip. I have my birthday coming and if the shipwrights doesn’t have a long list it could mean this project payout could be my birthday gift. If I ofc ship it 😅 devlog Small marketing changes everywhere to say that this project doesn’t have any ready code to use Added info about error logger on the landing page Scopes A long way from last devlog Fixed pytest ( A second time) TEMPORARY FIX added so the scope error doesn’t - throw just internal error FIXed the Oauth2 endpoint at least 3 times Added a WIP endpoint to edit scope data from API Fixed how cars are created so they work with both - Oauth and scopes stuff Only looking dashboard page for scopes compare I think I forgot to say something in last devlog config file if you would like to use different instance Toml oillevel to oilLever AI disclaimer that I used this project to learn tailwind Make the doors have different statuses in mean time Untracked WIKI Redo the home page Started working on stardance path A lot of misc that I don’t remember other I created a small modified volvo example app to show that my app works like the real one"}, {"post_id": 55226, "ts": "2026-08-26T20:27:11Z", "hours": 4.2503, "text": "Devlog Error logger added colors to the terminal print out using: \\033[31m \\033[0m Good to know that it is so easy to add colors to text added timestamps to log file and - between errors Changes how it reads body once again and fixed url encoded requests logging My additional information I put in detail in the json response (the real API can send it like that buttty I didn’t see it. info from docs ) additional changes: New scenarios mostly about service warnings You can add notes to the website thru config files Scopes I started working on scopes. What are they? They are really permissions (you can know them from discord bots or if you login thru GitHub etc.) I don’t want to put them in this first ship but I needed to work on something (while I work on wiki) and I CAN’T guarantee every thing works after adding them. You can see it in the scopes branch. what is done: I have made preparation so the additional database (dict) I use for OAuth can store more data have updated website func to work with changes and also pytest added all possible conve (connectivity API) scopes WIKI when I add everything to them I will finally try making a ship added the error logger page updated the sidebar scenario and snapshots page a lot of misc Bonus photos from vacation on baltic sea"}, {"post_id": 49931, "ts": "2026-08-18T21:59:53Z", "hours": 3.1292, "text": "Devlog There was a small (or big) problem in my playground. If an error was returned in the response, you could only see it if your app handled it or by printing the response. That’s why I added an error logger. It can show errors in two ways: Terminal Log file How it works There were two ways of doing it (from what I learned): Middleware that captures all responses (this could be helpful in the future). Exception handlers (this is what I chose). I use two handlers: @app.exception_handler(RequestValidationError) @app.exception_handler(HTTPException) It was a horrible time searching for why it didn’t work. At first, I worked in a separate file, but I couldn’t get the (fastapi) app variable into those functions. So I copied them into main file to make them work first. I had to change some JSON responses to HTTPException to make the second handler work. After I captured the first request and printed it, I worked on one function that writes a log and another that prints to the terminal. I also created a class to store useful data. It works like this: Exception handler-> ->Put data into class -> ->Create response -> ->Return response ->Create a background task that logs the error I create a background task so the response doesn’t wait or even worse fails In the end I found that I can wrap these func with this simple func to make them work in different file def setup_error_logging(app: FastAPI): Added a config option: To disable it completely. To disable only the log file. It also bypasses all internal endpoints to make the logs more readable. The best fail… 😅 I returned an exception instead of raising it. I spent a good 15 minutes wondering why the response had status code 200 with a JSON body saying status code 400 . response changes I also checked how the authorization errors look and added two new ones: Missing Authorization header. Invalid vcc-api-key header. WIKI (not counted) I focus on making a GitHub wiki (new docs) After ending them and the error logger. Its time for the first ship !!!!!!! Nest instability: I made a backup server : https://frog01-21185.wykr.es/ Its not ideal one, bc some asian countries are blocked but its shouldn’t have any downtime (I hope so) If an Admin read it why I must copy enter from different text input 😭"}, {"post_id": 48444, "ts": "2026-08-16T19:51:20Z", "hours": 2.4822, "text": "Devlog SITE changes I found that some number inputs didn’t use the utility classes. I gave them inputText because they are really the same; the only difference is the HTML type . I also forgot to update the reloadable fuel section. It now uses the shorter utility class. Made sure that all text in the header is the same size (they weren’t). I copied all changes back to the main branch. config changes Then I added a config file using the configparser library. also added an option to disable the FastAPI docs. I started working on Wiki: reworking docs.md into a GitHub Wiki with up to date information. Error logger, so you can see what’s wrong without printing the API response in your code. I had some problems with catching the fastapi automatic response but I will leave it for next devlog"}, {"post_id": 46236, "ts": "2026-08-13T10:31:45Z", "hours": 4.6175, "text": "Devlog I forgot to add that I worked on the tests in the test branch, not the main one. OAuth2 PKCE testing. Helped me find an error where, if the code was used (it was reset to \"\" ), it was accepted as \"\" . Now it correctly returns an error. OAuth2 flow testing. Warnings and diagnostics now have some simple checks too. And I merged it into main . Then I started cleaning up the Tailwind CSS. NOTE: they are in new branch dashboard/cleanup Why? Because I used the Play CDN in the past, so I couldn’t create utility classes in a CSS file. Soooo I didn’t. Andddd there were a lot of Tailwind classes that didn’t fit on my screen. Now that I started precompiling it, I could add them. I added 5 unique utility classes. All of them combined are used around ~120 times across all HTMLs. The longest one: @utility inputSelection { @apply w-full px-4 py-2 .5 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-100 focus : outline-none focus : ring-2 focus : ring-blue-300 focus : border-transparent transition-all appearance-none text-center [ text-align-last : center ] cursor-pointer ; } I also minified the CSS file. If I understand it, it removes anything the browser doesn’t need to understand the CSS (new lines, unnecessary spaces…). It’s better because those characters are still sent over the network, and fewer bytes mean less traffic. While using the new utility classes, I tried to preserve the layout, so everything should look the same as before."}, {"post_id": 44802, "ts": "2026-08-10T20:28:29Z", "hours": 3.7639, "text": "Devlog Working hard on pytest tests. I learn how assert works and how to name . I never used or created test in any language so I think they aren’t perfect (not like they should),but they work😄. I looked like AI made few and how other people and read some stuff online Tests created for these endpoints Around 25 of them List cars About car Engine info Engine start/stop Climate start/stop Windows Doors Lock/Unlock Reduced Guard Lock Flash, Honk, and Combination Tyres Statistics Fuel Odometer Engine diagnostics Brakes Command accessibility Still to do Warnings and diagnostics Commands More changes The tests helped me find and clean up several issues: Documentation updates. The honk endpoint was ../horn but should be /honk . Some values ( odometer , fuel ) are int , just like in the OpenAPI specification, not as described in the official docs. Fixed spelling mistakes: - oillevel to oilLevel - coolantLever to coolantLevel Want to help and has access to volvo with active VOC I need to test engine and climate endpoints but I don’t have an access to a car with it. Soooo if you can contact me on slack"}, {"post_id": 42512, "ts": "2026-08-07T09:41:54Z", "hours": 4.2411, "text": "Devlog Tracking headers implemented : There are two vcc-api-operationId - UUID traceparent - W3C traceparent (for now only sends it back) dashboard should look good on small screen (its very long but I don’t want to redesign it now) This is the reason why I there is much time in this devlog. It was a lot of try and error before something looked good both on mobile and desktop and I liked it . But at least it works know 🙃. some small cleanup of comments I worked again on internal updates endpoint (hopefully works now) started working on pytests (but I will talk about in next devlog (I gave my self 2 days for it) I really want to get the first ship out, so it isn’t going to be perfect. I still have a lot of features and improvements I’d like to add."}, {"post_id": 40711, "ts": "2026-08-04T12:41:43Z", "hours": 4.4231, "text": "Devlog I had a problem when I wanted to rewrite the scenario system. It only changed the local version of the Car (It means it really didn’t update anything). I wanted to update a backup version first, and only if everything was correct, replace the original with the backup. In the end, I used an already created function to first test and then apply to the car . Main PKCE misspelling fixed. Added a landing page with an option to disable it and redirect directly to the login screen (like in the last devlog I have photos of making it). Worked on the lights section. Fixed the docs URLs. Dashboard and WebSocket toggles now work. Rewrote and enabled the updates endpoint. Validation now catches values that can’t be converted to int . Scenario loading into a car instance should now be more consistent with the other functions. Improved mobile responsiveness (and then fixed the desktop one). Some elements were outside the screen on mobile (nothing important, but it didn’t look good). NOTE: the dashboard with car attributes still needs to be adapted. Added windows (because I forgot to 😅) to the dashboard and organised the lighting section. OAuth2 and Snapshots buttons should now always be centred. TODO Debug and tracing headers (from the real API). Additional instructions. Then I think it’s finally ready for the first ship. There are still a lot of things I want to add, but I can’t work indefinitely. Compare The compare app now has the Content-Type fix enabled (like the official API). Compiled the Tailwind CSS for the compare app. NOTE: I will add here more screenshots as link later Also thinking about adding it frictionless mission but I’m not sure."}, {"post_id": 39642, "ts": "2026-08-02T19:24:37Z", "hours": 3.62, "text": "Devlog Snapshots loading page (screenshots while I worked. I had more but the limit was 4). As suggested by the Tailwind CSS tools, changed some classes to more canonical ones. You can now delete cars through both the API and the dashboard. Snapshot corruption (when the app is force quit) should be fixed. Started working on the landing page, but I’ll leave it for the next devlog (mostly because I have on my other laptop). Fixes to do Sometimes the new OAuth2 and Snapshots buttons are off-center. Compare app needs fixing (need to enable the Content-Type fix from the official API). PKCE spelling in the internal API."}, {"post_id": 37827, "ts": "2026-07-30T19:39:54Z", "hours": 4.1028, "text": "Devlog The Content-Type problem is SOLVED …… What was the problem? (My reading skills? ) there are two headers with application/json : Content-Type (POST) Accept (GET) The Accept header isn’t required ( can be */* , application/* or application/json ). So the Content-Type mystery is mostly FIXED but I still need to check with a real Volvo car if Content-Type is actually required. (I’ll do it tomorrow.) Changes: Accept is now handled by my API like in the official API. Added Content-Type checks (like the docs say, meaning it’s required). While proofreading the docs, I found that PKCE is written as PCKE in one internal endpoint. (Not fixed yet.) Small README update (I try to keep it up to date). Scenario page: You can now load a scenario using the GUI. Recompiled the Tailwind CSS. I attached screenshots showing it (you can also see snapshot button but its not implemented YET)"}, {"post_id": 36608, "ts": "2026-07-28T20:31:07Z", "hours": 0.5122, "text": "The comparison app is now online! Comparison app: https://compareplay.kls.hackclub.app/ I also updated the main app with the new dashboard design: https://playground.kls.hackclub.app/internal/welcome devlog Tailwind CSS is no longer loaded from the CDN. I think its now precompiled. Note to others: Remember to update your banner to feature your app (see #stardance on Slack). Otherwise, your ship will be rejected. EDIT: A lot of bots try to connect to my apps do you think I should make a honeypot?"}, {"post_id": 36297, "ts": "2026-07-28T09:47:40Z", "hours": 3.2881, "text": "Devlog I found another way to hide the fuel stat when it isn’t useful for a particular type of car: the HTML hidden attribute. At first, I thought I should add or remove the element hidden, but that turned out to be more complicated than I expected. I think using hidden is still a cleaner solution but I still using templates files. While working on the dashboard, I wanted to add units like km , L , and % to some values. Instead of handling that in Python, I added another div with some margins and, in a few places, used flex so everything stays on the same line without adding more backend logic. Added more page changes thru HTMX (nearly all pages use it). Creating a new car now refreshes the page. when an error occurs, I use HX-Reswap and the HX-Retarget response header (when creating new car as proof of concept). Added HTMX transitions to the OAuth2 settings page.(all pages have transitions) What to do before the first release (Mostly a checklist for myself 😅) Content-Type checks. Scenario and snapshot system support in the dashboard. Main showcase page. Finish the remaining documentation. Two READMEs: one for the main repository and one for the companion app repository. I’m thinking about having one technical README and another one that’s easier to understand for people who don’t code. Better error handling in the dashboard. Right now, most errors are just displayed as plain text instead of being part of the page. deploy updated version on nest I wanted to add a video but MacOS made me a half gig and I don’t think stardance would handle"}, {"post_id": 35043, "ts": "2026-07-26T06:29:01Z", "hours": 5.5947, "text": "Devlog Most of my time was spent working on the dashboard with Tailwind CSS. It took a lot of experimenting to find a design I actually like. I also changed how HTMX updates the dashboard through WebSockets. Before: A dedicated template was rendered every time to update that part of the dashboard. Now: Only the text containing the updated value is replaced. That said, in the future it won’t always be just text. For example, if the fuel type changes from hybrid to electric , the fuel level should disappear from the dashboard instead of just changing its value. While working on this, I found a bug caused by HTMX sending boolean values as strings. This caused climate settings to be marked as invalid. I need to distinguish between an invalid value and an invalid attribute instead of always treating the entire attribute as invalid. Note to myself Add transitions. Make all HTML pages use the same CSS file from now on. Update the version on nest server and make a public companion instance Aaand that’s all. Making websites is painfully slow."}, {"post_id": 33472, "ts": "2026-07-23T13:34:25Z", "hours": 4.2786, "text": "Devlog I don’t know if I mentioned it before, but for now the Content-Type checks are disabled. I need to learn more about them and implement better validation. Created an updated OAuth2 settings page and a car selection screen (with Tailwind CSS, of course). Started experimenting with a new dashboard. It’s still a work in progress, but it will most likely become the main design. I’m also thinking about adding a second dashboard style, although I’m not sure if I’ll do that before the first release. I definitely want to clean up the HTML/Tailwind code by moving repeated styles into reusable classes or something similar. I’m also thinking about using more HTMX so that more pages can update data without needing a full page reload. Wrote more documentation. I still need to verify the fuel endpoint because I don’t remember whether it handles different engine types correctly. ( The new OAuth2 settings page and the car selection screen. For next develop I think I will make a video of how everything works)"}, {"post_id": 32303, "ts": "2026-07-21T11:58:32Z", "hours": 2.6278, "text": "Devlog Companion app I said it was finished for now, but I forgot to add one more thing (the one on the left in the screenshots): a dashboard redirect. Main app I’m still investigating the Content-Type problem. I verified that the official Volvo API behaves correctly, so I’m working on matching that behavior. Added work-in-progress Content-Type checks. Started reworking the CSS using Tailwind CSS. It’s still mixed with some plain CSS for now. Enabled support for the reduced guard lock command. You have to manually mark it as supported, and for now it behaves the same as the normal lock command. Let me know which design you like more: the new one (center) or the old one (right)."}, {"post_id": 31979, "ts": "2026-07-20T20:18:13Z", "hours": 3.7667, "text": "Devlog I’m mainly focused on finishing the companion app. I started working on the CSS. Most of my time was spent learning Tailwind CSS (so I didn’t track it). I think it’s a better fit for me than writing plain CSS. Added Tailwind CSS to all pages of the companion app. (I think I need to redo the main app now. 😅) Added a small README with information about the main app. I think it done now but I will come back to it before first ship Main app While working on the companion app, I found another issue. The Content-Type: application/json behavior doesn’t match the official API. I verified that the official API works as expected, checks for it mine doesn’t (I think)"}, {"post_id": 30890, "ts": "2026-07-18T21:12:58Z", "hours": 3.2169, "text": "Devlog Companion app Added manual mode for comparison. Engine start/stop now works in normal mode. Before pushing, I forgot to fix one thing: { \"error\" : \"Failed to retrieve data from unofficial API.\" , \"detail\" :{ \"detail\" :[{ \"type\" : \"dict_type\" , \"loc\" :[ \"body\" ], \"msg\" : \"Input should be a valid dictionary\" , \"input\" : \"{ \\\" runtimeMinutes \\\" : 10}\" }]}} The fix was simple: I forgot to use json.loads() . I pushed a quick fix commit right after. The photo shows the manual comparison page. I’m still working on the comparison logic, which is why it doesn’t have any CSS yet. Sorry for not adding Volvo API keys so you can see the differences between both APIs, but that would have taken too much time today. Its also the reason why Im creating this app Main app Worked on the documentation. The notifier now supports more than one updated attribute at the same time. It’s not the best implementation, but I’ll improve it after the first ship. Fixed snapshot loading and added exception handling. Scenario loading now reports malformed JSON files. Fixed a dashboard bug where tire information was shown three times. I think I planned to add more data there… and then forgot. I should probably test everything before making a devlog or committing, but I also don’t want my devlogs to have to much time."}, {"post_id": 29435, "ts": "2026-07-16T11:46:44Z", "hours": 0.5936, "text": "A public online demo!!!!! The project is finally available online! https://playground.kls.hackclub.app/internal/welcome Please don’t be the bad guy and fight with other people over access. ❤️ That’s all I wanted to say because this is a really big milestone for me. There will also be another commit today to make sure everyone can run their own instance of the project. I found a few issues that only appeared outside of my local environment: Some WebSocket features break when using HTTPS. The dashboard had problems when the instance wasn’t running on the same computer. Those should be fixed in today’s commit. If you find any errors please comment or contact me on slack Also thanks to nest hackclub for hosting"}, {"post_id": 29164, "ts": "2026-07-15T21:37:24Z", "hours": 2.8483, "text": "Devlog Authorization was my mistake. It should have been accessToken , not Authorization . Why I thought that? I have no idea. No it was good before I was sleepy I think when I did that The snapshots system now works. Why it didn’t work as expected: I forgot to use copy(deep=True) . One of the small dictionaries was written incorrectly. Added saving and loading snapshots from a file. The scenarios system now works and has been tested. Difference between snapshots and scenarios Snapshots copy all data about a car. They are persistent and can be saved to a file. Scenarios only change selected attributes. They are designed to be created and edited manually in a JSON file. Main app The main app is mostly ready. Before the first release, I still need to: Finish the documentation. Add a roadmap with future updates. Verify error responses against the real API. Finish the comparison feature in the companion app. Add more ready-to-use scenarios and API keys. Companion app Manual login now works, so only the comparison logic is left. I found a bug where api_key_p was None because one endpoint redirected using api_key instead. Started working on the comparison feature. PS: A photo of why the api_key_p was none"}, {"post_id": 27883, "ts": "2026-07-13T19:57:06Z", "hours": 3.8994, "text": "Devlog Writing documentation is a good way to check whether the code you wrote actually works. I already found four bugs while documenting the project. For example, I forgot to pass the redirect URI in a function in main.py , even though it was already implemented in internal.py . I also fixed a bug where notifications could sometimes contain an older value. The dashboard has been moved into a separate file. Snapshots currently have a problem. They are very similar to the Car class, so I’ll debug it and explain what the issue was in the next devlog. Companion app The companion app is meant to showcase the differences between my implementation and the official Volvo Demo Car Sandbox. It also makes testing easier, since you don’t have to write your own application just to try the API. I had a problem with the built-in LibreSSL because one of the dependencies didn’t support it. I had to reinstall Python so that uv would detect the new OpenSSL installation. After that, I was finally able to start the app and get it working. Auto-login and automatic creation of a test car are now working. There’s is no css but I will do it after logic works One more thing: the reason the devlogs have been quieter recently is that I’ve been dealing with some administrative paperwork. I’ll get back to posting them more regularly once that’s sorted out."}, {"post_id": 25868, "ts": "2026-07-10T10:58:56Z", "hours": 4.3936, "text": "Devlog How I don’t like web development… did I already say that? I’ll say it again. I’m working on the OAuth2 settings, and I had a problem where one of the query parameters wasn’t reaching the backend. I still don’t know what actually fixed it. Then I had another problem. I have a function that changes values in the backend, but only one button worked while everything else didn’t. It turned out to be because I debugged the first problem with AI. Always trust your own mind before trusting AI. The problem was hx-trigger . I also had problems applying CSS through HTMX. In the end, I solved it by using hx-redirect . But in the end everything works (you can see it one the phtoto) I also worked on the scenario system, which loads scenarios from a JSON file. It isn’t tested yet, and I still need to work on state snapshots. The idea is that you’ll be able to save a moment in time and restore it later, so you can test the same scenario as many times as you want without setting up all the data again. I also started working on a companion app to showcase the differences between my implementation and the official demo car sandbox. I’ll write more about it in the next devlog."}, {"post_id": 24338, "ts": "2026-07-07T20:58:43Z", "hours": 3.7069, "text": "Devlog A small error from the last devlog: it’s PKCE , not PCKE . I had to rename a lot of variables because of that. More customizable data Gearbox Model year More diagnostic data for lights (hazard, fog, brake, high beam, low beam, and more) Other additions Added a new default API key with 3 cars, each having different values. Wrote more docs. Started working on scenarios (templates for new or existing cars to make setup faster). I’m thinking about loading them from a file so users can create their own. Started working on setting up OAuth2 through the dashboard. Fixed most command responses because when I first created them, I forgot to wrap everything inside { \"data\": {} } . Added light options to the dashboard. Working: PKCE How does it work? The application generates a random secret. Before redirecting the user to the login page, it sends only a hashed version of that secret. After the user logs in, the application receives an authorization code. When exchanging the code for tokens, it sends the original secret. The server checks whether it matches the previously received hash. If it does, the tokens are issued. It normally protects from having the tokens stolen New promoting picture I made in canva have a look"}, {"post_id": 23100, "ts": "2026-07-05T20:54:08Z", "hours": 3.3289, "text": "Devlog Small additions to the new documentation. changes how the api handles errors BIG ADDITION OAuth2 without PKCE. I started working on PKCE, but I thought I should write a devlog before adding it. Added a login website (I know I mentioned it last time). Added a way to enable and disable OAuth2. Authentication now checks whether OAuth2 is enabled. If it is, requests are authenticated using OAuth2. Tokens don’t expire yet, but that will be added soon. Added endpoints for refreshing tokens and exchanging an authorization code for tokens. How does OAuth2 work? The app redirects the user to a login page. After logging in, the website redirects the user back to the callback URL provided by the app. The app receives an authorization code, which it exchanges for two tokens: a long-lived refresh token and a short-lived access token. When the access token expires, the app can use the refresh endpoint to obtain a new access token. What is PKCE? I’ll write more about it once it’s added to the project. In short, PKCE protects the authorization flow by ensuring that only the application that started the login process can exchange the authorization code for tokens. The application first generates a random secret (called a code verifier). Before the login request is sent, a hashed version of that secret (the code challenge) is sent to the server. Later, when exchanging the authorization code for tokens, the application sends the original secret. The server verifies that it matches the previously received hash before issuing any tokens. I’ll explain the complete flow in the next devlog."}, {"post_id": 20221, "ts": "2026-07-01T19:28:27Z", "hours": 5.3022, "text": "Devlog This devlog will be a short one because I’ve been focusing on implementing OAuth 2.0. I’ll cover that in more detail in the next update but I will add photo of a “login” screen that I created for it (it looks bad on purpose). What’s new The terminal now works with more than one static car. The dashboard can (once again) create new cars. The codebase has been split into multiple files instead of a single large file: main still contains the public API endpoints. internal contains the dashboard logic and internal API endpoints. database will contain multiple predefined car scenarios. I also started writing a new manual that documents the quirks of the Volvo API and my implementation ( and edge cases). I will write more about in next 2 devlogs What’s left before the first ship An example application demonstrating the differences between the official Volvo Sandbox and this project. OAuth 2.0 with PKCE (it would be great to have before the first ship). Documentation. A major code cleanup and refactoring."}, {"post_id": 17924, "ts": "2026-06-28T21:18:15Z", "hours": 2.7358, "text": "Devlog I added two static (for now) endpoints: statistics and warning . The statistics endpoint mostly returns data, so there isn’t much to test. The warning endpoint has a very large response that would require a lot of new code, and for now I don’t want to implement all of it. I started working on adding cars through the dashboard (you could already see part of it in the last devlog), but I found out it doesn’t work, so I need to fix it before the next devlog. Climate and engine STOP commands now respond correctly. In the last invoice update, they incorrectly returned RUNNING . Added checks for the new command endpoints to verify whether the car supports a given command. Added a small terminal page that outputs changes. In the future, you’ll be able to choose whether to display only Volvo changes, all changes, or all information (for example, if you checked whether the windows status). I also used it to verify that the normal WebSocket status works. Added the foundations for disabling WebSockets on startup. Only a configuration file is left to implement. Added UNAVAILABLE reason invoice logic mostly something like this: If the reason is NO_INTERNET , the invoice becomes CONNECTION_FAILURE . If the reason is POWER_SAVING_MODE , the invoice becomes VEHICLE_IN_SLEEP . Made some improvements to the dashboard’s appearance."}, {"post_id": 15774, "ts": "2026-06-25T17:19:10Z", "hours": 7.1422, "text": "Devlog A funny story: I built the invoice system but didn’t always assign values correctly, so the values got mixed up. I spent a long time debugging before realizing that was the cause. { \"error\" : { \"message\" : \"VALUE_ERROR\" , \"description\" : \" \\\" Car \\\" object has no field \\\" 123 \\\" \" } } I also spent a lot of time on CSS. Most of that time went into learning: classes IDs box shadows CSS Grid The reason it took so long since the last devlog is CSS. As I said before, I don’t really like web development, so my motivation was pretty low. I used some AI, but I worked hard to remove most of the generated code. At this point, most of the site’s design looks the way I want it to and will probably stay that way until the end of the project. Made invoices have some logic instead of always sending the same value. That means: If no invoice status is set, it sends COMPLETED or RUNNING depending on whether the engine or climate is running. Loc like actions can’t have a RUNNING invoice status. Added a distinction between successful and failed invoices. PS: Always remember to stop the server with Ctrl+C , not Ctrl+Z , because Ctrl+Z can leave the process running and keep the port blocked."}, {"post_id": 14241, "ts": "2026-06-23T07:56:55Z", "hours": 6.0522, "text": "Devlog It has been a long time since my last devlog, but I was more focused on learning than adding new stuff. Right now, I am focusing on learning HTML, htmx, and Jinja. The welcome page logic is mostly working. I had some problems with it because I used htmx to add new code, but the JavaScript from the is not reloaded with that. Now, I use the HX-Redirect response. I also learned how WebSockets work with FastAPI and htmx. I also designed how the dashboard will look (I attached my drawings. Good luck reading them!)."}, {"post_id": 11274, "ts": "2026-06-18T13:16:22Z", "hours": 3.5742, "text": "Devlog centralLock is called carLocked in the docs. The docs seem to be outdated. I think the OpenAPI specification is more accurate and contains fewer errors. I added a way to validate data using class functions. Implemented timestamps. Using datetime function from python Added tyre endpoints. Added a way to generate new API keys and create new cars (I used python secrets). (getting ready to make first public running version) Roadmap.md file with status of this project What is next to do Start using the updated method for changing data. Update the dashboard to reflect and modify the new data. Add WebSocket support. Add the remaining 2 vehicle and warning endpoints. Create an example app for testers. Redesign dashboard. It looks awful I had a 2 days off because I was on school trip but I back. Next thing on my list is an updated dashboard"}, {"post_id": 9202, "ts": "2026-06-15T13:28:28Z", "hours": 1.9225, "text": "Devlog You can edit data through the dashboard now. It’s not the best solution, and I want to change how it works (probably start from scratch), but not now. Diagnostics endpoints. I had a very good time with them today. Why? Because of the “/diagnostics” docs. I attached a screenshot showing an example from the official website and data from a real car (left: real API, right: docs). If you look at them, you can see two errors and one very small issue that you would only notice if you read the whole documentation: In serviceWarning, you can see the value is NORMAL, but NORMAL is not a valid value for it. It should be NO_WARNING. This is the small one. In engineHoursToService, the API response contains a unit, but the docs don’t mention it at all. And the worst one (and the only real error): there is no information about washerFluidLevelWarning in the docs, but it appears in the data from the car. What do you think I should do? Should I write to them about it? I think you can comment on the post, so let me know what you think."}, {"post_id": 8961, "ts": "2026-06-14T22:15:21Z", "hours": 1.7606, "text": "Devlog Added odometer and fuel endpoints. Funny thing is that Volvo sends in what unit the data is, but the docs say that only metric units are valid, so it really sends always the same unit. Most likely they wanted to add imperial later, or they left it like this for simplicity. Added first version of dashboard. For now, it’s using polling every 5 seconds (I have planned to change it to WebSocket). It only displays some information and can’t edit anything. Also, I wrote some HTML and JavaScript. I never liked web development, so I don’t know a lot (I’m learning while I code 😅). It could be that it will look very rough till the end because I don’t have any patience for HTML and CSS. I “will” do it when I have a very good day, or AI will do it? I would like not to use AI in this way, but I will see. Edit: Forgot to push the updated version to GitHub I will do it tomorrow because it’s late (it’s tomorrow )"}, {"post_id": 8322, "ts": "2026-06-13T20:45:50Z", "hours": 2.8264, "text": "Devlog Added a command accessibility endpoint (it tells if the car can receive commands). I refactored duplicate code in the climate endpoint. Added engine endpoints (they aren’t perfect, but this isn’t the final code). It was also a frustrating day because I repeatedly forgot to save my code before testing (my bad). A photo of the error that cost me way too much time today. (I changed it to “/vehicles/{VIN}/engine-status”, but I didn’t save the file.)"}, {"post_id": 7410, "ts": "2026-06-12T10:41:57Z", "hours": 0.9136, "text": "I setup my codespace and started working on first few endpoints. Additionally I looked how most of Volvos apis looks that gave me direction to go. That was all time I had today. Also I could add screenshot here because of macOS :("}]...
|
Title: Volvo API playground by @Kl | Stardance
De Title: Volvo API playground by @Kl | Stardance
Devlogs:
I setup my codespace and started working on first few endpoints. Additionally I looked how most of Volvos apis looks that gave me direction to go. That was all time I had today. Also I could add screenshot here because of macOS :(
Devlog Added a command accessibility endpoint (it tells if the car can receive commands). I refactored duplicate code in the climate endpoint. Added engine endpoints (they aren’t perfect, but this isn’t the final code). It was also a frustrating day because I repeatedly forgot to save my code before testing (my bad). A photo of the error that cost me way too much time today. (I changed it to “/vehicles/{VIN}/engine-status”, but I didn’t save the file.)
Devlog Added odometer and fuel endpoints. Funny thing is that Volvo sends in what unit the data is, but the docs say that only metric units are valid, so it really sends always the same unit. Most likely they wanted to add imperial later, or they left it like this for simplicity. Added first version of dashboard. For now, it’s using polling every 5 seconds (I have planned to change it to WebSocket). It only displays some information and can’t edit anything. Also, I wrote some HTML and JavaScript. I never liked web development, so I don’t know a lot (I’m learning while I code 😅). It could be that it will look very rough till the end because I don’t have any patience for HTML and CSS. I “will” do it when I have a very good day, or AI will do it? I would like not to use AI in this way, but I will see. Edit: Forgot to push the updated version to GitHub I will do it tomorrow because it’s late (it’s tomorrow )
Devlog You can edit data through the dashboard now. It’s not the best solution, and I want to change how it works (probably start from scratch), but not now. Diagnostics endpoints. I had a very good time with them today. Why? Because of the “/diagnostics” docs. I attached a screenshot showing an example from the official website and data from a real car (left: real API, right: docs). If you look at them, you can see two errors and one very small issue that you would only notice if you read the whole documentation: In serviceWarning, you can see the value is NORMAL, but NORMAL is not a valid value for it. It should be NO_WARNING. This is the small one. In engineHoursToService, the API response contains a unit, but the docs don’t mention it at all. And the worst one (and the only real error): there is no information about washerFluidLevelWarning in the docs, but it appears in the data from the car. What do you think I should do? Should I write to them about it? I think you can comment on the post, so let me know what you think.
Devlog centralLock is called carLocked in the docs. The docs seem to be outdated. I think the OpenAPI specification is more accurate and contains fewer errors. I added a way to validate data using class functions. Implemented timestamps. Using datetime function from python Added tyre endpoints. Added a way to generate new API keys and create new cars (I used python secrets). (getting ready to make first public running version) Roadmap.md file with status of this project What is next to do Start using the updated method for changing data. Update the dashboard to reflect and modify the new data. Add WebSocket support. Add the remaining 2 vehicle and warning endpoints. Create an example app for testers. Redesign dashboard. It looks awful I had a 2 days off because I was on school trip but I back. Next thing on my list is an updated dashboard
Devlog It has been a long time since my last devlog, but I was more focused on learning than adding new stuff. Right now, I am focusing on learning HTML, htmx, and Jinja. The welcome page logic is mostly working. I had some problems with it because I used htmx to add new code, but the JavaScript from the is not reloaded with that. Now, I use the HX-Redirect response. I also learned how WebSockets work with FastAPI and htmx. I also designed how the dashboard will look (I attached my drawings. Good luck reading them!).
Devlog A funny story: I built the invoice system but didn’t always assign values correctly, so the values got mixed up. I spent a long time debugging before realizing that was the cause. { "error" : { "message" : "VALUE_ERROR" , "description" : " \" Car \" object has no field \" 123 \" " } } I also spent a lot of time on CSS. Most of that time went into learning: classes IDs box shadows CSS Grid The reason it took so long since the last devlog is CSS. As I said before, I don’t really like web development, so my motivation was pretty low. I used some AI, but I worked hard to remove most of the generated code. At this point, most of the site’s design looks the way I want it to and will probably stay that way until the end of the project. Made invoices have some logic instead of always sending the same value. That means: If no invoice status is set, it sends COMPLETED or RUNNING depending on whether the engine or climate is running. Loc like actions can’t have a RUNNING invoice status. Added a distinction between successful and failed invoices. PS: Always remember to stop the server with Ctrl+C , not Ctrl+Z , because Ctrl+Z can leave the process running and keep the port blocked.
Devlog I added two static (for now) endpoints: statistics and warning . The statistics endpoint mostly returns data, so there isn’t much to test. The warning endpoint has a very large response that would require a lot of new code, and for now I don’t want to implement all of it. I started working on adding cars through the dashboard (you could already see part of it in the last devlog), but I found out it doesn’t work, so I need to fix it before the next devlog. Climate and engine STOP commands now respond correctly. In the last invoice update, they incorrectly returned RUNNING . Added checks for the new command endpoints to verify whether the car supports a given command. Added a small terminal page that outputs changes. In the future, you’ll be able to choose whether to display only Volvo changes, all changes, or all information (for example, if you checked whether the windows status). I also used it to verify that the normal WebSocket status works. Added the foundations for disabling WebSockets on startup. Only a configuration file is left to implement. Added UNAVAILABLE reason invoice logic mostly something like this: If the reason is NO_INTERNET , the invoice becomes CONNECTION_FAILURE . If the reason is POWER_SAVING_MODE , the invoice becomes VEHICLE_IN_SLEEP . Made some improvements to the dashboard’s appearance.
Devlog This devlog will be a short one because I’ve been focusing on implementing OAuth 2.0. I’ll cover that in more detail in the next update but I will add photo of a “login” screen that I created for it (it looks bad on purpose). What’s new The terminal now works with more than one static car. The dashboard can (once again) create new cars. The codebase has been split into multiple files instead of a single large file: main still contains the public API endpoints. internal contains the dashboard logic and internal API endpoints. database will contain multiple predefined car scenarios. I also started writing a new manual that documents the quirks of the Volvo API and my implementation ( and edge cases). I will write more about in next 2 devlogs What’s left before the first ship An example application demonstrating the differences between the official Volvo Sandbox and this project. OAuth 2.0 with PKCE (it would be great to have before the first ship). Documentation. A major code cleanup and refactoring.
Devlog Small additions to the new documentation. changes how the api handles errors BIG ADDITION OAuth2 without PKCE. I started working on PKCE, but I thought I should write a devlog before adding it. Added a login website (I know I mentioned it last time). Added a way to enable and disable OAuth2. Authentication now checks whether OAuth2 is enabled. If it is, requests are authenticated using OAuth2. Tokens don’t expire yet, but that will be added soon. Added endpoints for refreshing tokens and exchanging an authorization code for tokens. How does OAuth2 work? The app redirects the user to a login page. After logging in, the website redirects the user back to the callback URL provided by the app. The app receives an authorization code, which it exchanges for two tokens: a long-lived refresh token and a short-lived access token. When the access token expires, the app can use the refresh endpoint to obtain a new access token. What is PKCE? I’ll write more about it once it’s added to the project. In short, PKCE protects the authorization flow by ensuring that only the application that started the login process can exchange the authorization code for tokens. The application first generates a random secret (called a code verifier). Before the login request is sent, a hashed version of that secret (the code challenge) is sent to the server. Later, when exchanging the authorization code for tokens, the application sends the original secret. The server verifies that it matches the previously received hash before issuing any tokens. I’ll explain the complete flow in the next devlog.
Devlog A small error from the last devlog: it’s PKCE , not PCKE . I had to rename a lot of variables because of that. More customizable data Gearbox Model year More diagnostic data for lights (hazard, fog, brake, high beam, low beam, and more) Other additions Added a new default API key with 3 cars, each having different values. Wrote more docs. Started working on scenarios (templates for new or existing cars to make setup faster). I’m thinking about loading them from a file so users can create their own. Started working on setting up OAuth2 through the dashboard. Fixed most command responses because when I first created them, I forgot to wrap everything inside { "data": {} } . Added light options to the dashboard. Working: PKCE How does it work? The application generates a random secret. Before redirecting the user to the login page, it sends only a hashed version of that secret. After the user logs in, the application receives an authorization code. When exchanging the code for tokens, it sends the original secret. The server checks whether it matches the previously received hash. If it does, the tokens are issued. It normally protects from having the tokens stolen New promoting picture I made in canva have a look
Devlog How I don’t like web development… did I already say that? I’ll say it again. I’m working on the OAuth2 settings, and I had a problem where one of the query parameters wasn’t reaching the backend. I still don’t know what actually fixed it. Then I had another problem. I have a function that changes values in the backend, but only one button worked while everything else didn’t. It turned out to be because I debugged the first problem with AI. Always trust your own mind before trusting AI. The problem was hx-trigger . I also had problems applying CSS through HTMX. In the end, I solved it by using hx-redirect . But in the end everything works (you can see it one the phtoto) I also worked on the scenario system, which loads scenarios from a JSON file. It isn’t tested yet, and I still need to work on state snapshots. The idea is that you’ll be able to save a moment in time and restore it later, so you can test the same scenario as many times as you want without setting up all the data again. I also started working on a companion app to showcase the differences between my implementation and the official demo car sandbox. I’ll write more about it in the next devlog.
Devlog Writing documentation is a good way to check whether the code you wrote actually works. I already found four bugs while documenting the project. For example, I forgot to pass the redirect URI in a function in main.py , even though it was already implemented in internal.py . I also fixed a bug where notifications could sometimes contain an older value. The dashboard has been moved into a separate file. Snapshots currently have a problem. They are very similar to the Car class, so I’ll debug it and explain what the issue was in the next devlog. Companion app The companion app is meant to showcase the differences between my implementation and the official Volvo Demo Car Sandbox. It also makes testing easier, since you don’t have to write your own application just to try the API. I had a problem with the built-in LibreSSL because one of the dependencies didn’t support it. I had to reinstall Python so that uv would detect the new OpenSSL installation. After that, I was finally able to start the app and get it working. Auto-login and automatic creation of a test car are now working. There’s is no css but I will do it after logic works One more thing: the reason the devlogs have been quieter recently is that I’ve been dealing with some administrative paperwork. I’ll get back to posting them more regularly once that’s sorted out.
Devlog Authorization was my mistake. It should have been accessToken , not Authorization . Why I thought that? I have no idea. No it was good before I was sleepy I think when I did that The snapshots system now works. Why it didn’t work as expected: I forgot to use copy(deep=True) . One of the small dictionaries was written incorrectly. Added saving and loading snapshots from a file. The scenarios system now works and has been tested. Difference between snapshots and scenarios Snapshots copy all data about a car. They are persistent and can be saved to a file. Scenarios only change selected attributes. They are designed to be created and edited manually in a JSON file. Main app The main app is mostly ready. Before the first release, I still need to: Finish the documentation. Add a roadmap with future updates. Verify error responses against the real API. Finish the comparison feature in the companion app. Add more ready-to-use scenarios and API keys. Companion app Manual login now works, so only the comparison logic is left. I found a bug where api_key_p was None because one endpoint redirected using api_key instead. Started working on the comparison feature. PS: A photo of why the api_key_p was none
A public online demo!!!!! The project is finally available online! https://playground.kls.hackclub.app/internal/welcome Please don’t be the bad guy and fight with other people over access. ❤️ That’s all I wanted to say because this is a really big milestone for me. There will also be another commit today to make sure everyone can run their own instance of the project. I found a few issues that only appeared outside of my local environment: Some WebSocket features break when using HTTPS. The dashboard had problems when the instance wasn’t running on the same computer. Those should be fixed in today’s commit. If you find any errors please comment or contact me on slack Also thanks to nest hackclub for hosting
Devlog Companion app Added manual mode for comparison. Engine start/stop now works in normal mode. Before pushing, I forgot to fix one thing: { "error" : "Failed to retrieve data from unofficial API." , "detail" :{ "detail" :[{ "type" : "dict_type" , "loc" :[ "body" ], "msg" : "Input should be a valid dictionary" , "input" : "{ \" runtimeMinutes \" : 10}" }]}} The fix was simple: I forgot to use json.loads() . I pushed a quick fix commit right after. The photo shows the manual comparison page. I’m still working on the comparison logic, which is why it doesn’t have any CSS yet. Sorry for not adding Volvo API keys so you can see the differences between both APIs, but that would have taken too much time today. Its also the reason why Im creating this app Main app Worked on the documentation. The notifier now supports more than one updated attribute at the same time. It’s not the best implementation, but I’ll improve it after the first ship. Fixed snapshot loading and added exception handling. Scenario loading now reports malformed JSON files. Fixed a dashboard bug where tire information was shown three times. I think I planned to add more data there… and then forgot. I should probably test everything before making a devlog or committing, but I also don’t want my devlogs to have to much time.
Devlog I’m mainly focused on finishing the companion app. I started working on the CSS. Most of my time was spent learning Tailwind CSS (so I didn’t track it). I think it’s a better fit for me than writing plain CSS. Added Tailwind CSS to all pages of the companion app. (I think I need to redo the main app now. 😅) Added a small README with information about the main app. I think it done now but I will come back to it before first ship Main app While working on the companion app, I found another issue. The Content-Type: application/json behavior doesn’t match the official API. I verified that the official API works as expected, checks for it mine doesn’t (I think)
Devlog Companion app I said it was finished for now, but I forgot to add one more thing (the one on the left in the screenshots): a dashboard redirect. Main app I’m still investigating the Content-Type problem. I verified that the official Volvo API behaves correctly, so I’m working on matching that behavior. Added work-in-progress Content-Type checks. Started reworking the CSS using Tailwind CSS. It’s still mixed with some plain CSS for now. Enabled support for the reduced guard lock command. You have to manually mark it as supported, and for now it behaves the same as the normal lock command. Let me know which design you like more: the new one (center) or the old one (right).
Devlog I don’t know if I mentioned it before, but for now the Content-Type checks are disabled. I need to learn more about them and implement better validation. Created an updated OAuth2 settings page and a car selection screen (with Tailwind CSS, of course). Started experimenting with a new dashboard. It’s still a work in progress, but it will most likely become the main design. I’m also thinking about adding a second dashboard style, although I’m not sure if I’ll do that before the first release. I definitely want to clean up the HTML/Tailwind code by moving repeated styles into reusable classes or something similar. I’m also thinking about using more HTMX so that more pages can update data without needing a full page reload. Wrote more documentation. I still need to verify the fuel endpoint because I don’t remember whether it handles different engine types correctly. ( The new OAuth2 settings page and the car selection screen. For next develop I think I will make a video of how everything works)
Devlog Most of my time was spent working on the dashboard with Tailwind CSS. It took a lot of experimenting to find a design I actually like. I also changed how HTMX updates the dashboard through WebSockets. Before: A dedicated template was rendered every time to update that part of the dashboard. Now: Only the text containing the updated value is replaced. That said, in the future it won’t always be just text. For example, if the fuel type changes from hybrid to electric , the fuel level should disappear from the dashboard instead of just changing its value. While working on this, I found a bug caused by HTMX sending boolean values as strings. This caused climate settings to be marked as invalid. I need to distinguish between an invalid value and an invalid attribute instead of always treating the entire attribute as invalid. Note to myself Add transitions. Make all HTML pages use the same CSS file from now on. Update the version on nest server and make a public companion instance Aaand that’s all. Making websites is painfully slow.
Devlog I found another way to hide the fuel stat when it isn’t useful for a particular type of car: the HTML hidden attribute. At first, I thought I should add or remove the element hidden, but that turned out to be more complicated than I expected. I think using hidden is still a cleaner solution but I still using templates files. While working on the dashboard, I wanted to add units like km , L , and % to some values. Instead of handling that in Python, I added another div with some margins and, in a few places, used flex so everything stays on the same line without adding more backend logic. Added more page changes thru HTMX (nearly all pages use it). Creating a new car now refreshes the page. when an error occurs, I use HX-Reswap and the HX-Retarget response header (when creating new car as proof of concept). Added HTMX transitions to the OAuth2 settings page.(all pages have transitions) What to do before the first release (Mostly a checklist for myself 😅) Content-Type checks. Scenario and snapshot system support in the dashboard. Main showcase page. Finish the remaining documentation. Two READMEs: one for the main repository and one for the companion app repository. I’m thinking about having one technical README and another one that’s easier to understand for people who don’t code. Better error handling in the dashboard. Right now, most errors are just displayed as plain text instead of being part of the page. deploy updated version on nest I wanted to add a video but MacOS made me a half gig and I don’t think stardance would handle
The comparison app is now online! Comparison app: https://compareplay.kls.hackclub.app/ I also updated the main app with the new dashboard design: https://playground.kls.hackclub.app/internal/welcome devlog Tailwind CSS is no longer loaded from the CDN. I think its now precompiled. Note to others: Remember to update your banner to feature your app (see #stardance on Slack). Otherwise, your ship will be rejected. EDIT: A lot of bots try to connect to my apps do you think I should make a honeypot?
Devlog The Content-Type problem is SOLVED …… What was the problem? (My reading skills? ) there are two headers with application/json : Content-Type (POST) Accept (GET) The Accept header isn’t required ( can be */* , application/* or application/json ). So the Content-Type mystery is mostly FIXED but I still need to check with a real Volvo car if Content-Type is actually required. (I’ll do it tomorrow.) Changes: Accept is now handled by my API like in the official API. Added Content-Type checks (like the docs say, meaning it’s required). While proofreading the docs, I found that PKCE is written as PCKE in one internal endpoint. (Not fixed yet.) Small README update (I try to keep it up to date). Scenario page: You can now load a scenario using the GUI. Recompiled the Tailwind CSS. I attached screenshots showing it (you can also see snapshot button but its not implemented YET)
Devlog Snapshots loading page (screenshots while I worked. I had more but the limit was 4). As suggested by the Tailwind CSS tools, changed some classes to more canonical ones. You can now delete cars through both the API and the dashboard. Snapshot corruption (when the app is force quit) should be fixed. Started working on the landing page, but I’ll leave it for the next devlog (mostly because I have on my other laptop). Fixes to do Sometimes the new OAuth2 and Snapshots buttons are off-center. Compare app needs fixing (need to enable the Content-Type fix from the official API). PKCE spelling in the internal API.
Devlog I had a problem when I wanted to rewrite the scenario system. It only changed the local version of the Car (It means it really didn’t update anything). I wanted to update a backup version first, and only if everything was correct, replace the original with the backup. In the end, I used an already created function to first test and then apply to the car . Main PKCE misspelling fixed. Added a landing page with an option to disable it and redirect directly to the login screen (like in the last devlog I have photos of making it). Worked on the lights section. Fixed the docs URLs. Dashboard and WebSocket toggles now work. Rewrote and enabled the updates endpoint. Validation now catches values that can’t be converted to int . Scenario loading into a car instance should now be more consistent with the other functions. Improved mobile responsiveness (and then fixed the desktop one). Some elements were outside the screen on mobile (nothing important, but it didn’t look good). NOTE: the dashboard with car attributes still needs to be adapted. Added windows (because I forgot to 😅) to the dashboard and organised the lighting section. OAuth2 and Snapshots buttons should now always be centred. TODO Debug and tracing headers (from the real API). Additional instructions. Then I think it’s finally ready for the first ship. There are still a lot of things I want to add, but I can’t work indefinitely. Compare The compare app now has the Content-Type fix enabled (like the official API). Compiled the Tailwind CSS for the compare app. NOTE: I will add here more screenshots as link later Also thinking about adding it frictionless mission but I’m not sure.
Devlog Tracking headers implemented : There are two vcc-api-operationId - UUID traceparent - W3C traceparent (for now only sends it back) dashboard should look good on small screen (its very long but I don’t want to redesign it now) This is the reason why I there is much time in this devlog. It was a lot of try and error before something looked good both on mobile and desktop and I liked it . But at least it works know 🙃. some small cleanup of comments I worked again on internal updates endpoint (hopefully works now) started working on pytests (but I will talk about in next devlog (I gave my self 2 days for it) I really want to get the first ship out, so it isn’t going to be perfect. I still have a lot of features and improvements I’d like to add.
Devlog Working hard on pytest tests. I learn how assert works and how to name . I never used or created test in any language so I think they aren’t perfect (not like they should),but they work😄. I looked like AI made few and how other people and read some stuff online Tests created for these endpoints Around 25 of them List cars About car Engine info Engine start/stop Climate start/stop Windows Doors Lock/Unlock Reduced Guard Lock Flash, Honk, and Combination Tyres Statistics Fuel Odometer Engine diagnostics Brakes Command accessibility Still to do Warnings and diagnostics Commands More changes The tests helped me find and clean up several issues: Documentation updates. The honk endpoint was ../horn but should be /honk . Some values ( odometer , fuel ) are int , just like in the OpenAPI specification, not as described in the official docs. Fixed spelling mistakes: - oillevel to oilLevel - coolantLever to coolantLevel Want to help and has access to volvo with active VOC I need to test engine and climate endpoints but I don’t have an access to a car with it. Soooo if you can contact me on slack
Devlog I forgot to add that I worked on the tests in the test branch, not the main one. OAuth2 PKCE testing. Helped me find an error where, if the code was used (it was reset to "" ), it was accepted as "" . Now it correctly returns an error. OAuth2 flow testing. Warnings and diagnostics now have some simple checks too. And I merged it into main . Then I started cleaning up the Tailwind CSS. NOTE: they are in new branch dashboard/cleanup Why? Because I used the Play CDN in the past, so I couldn’t create utility classes in a CSS file. Soooo I didn’t. Andddd there were a lot of Tailwind classes that didn’t fit on my screen. Now that I started precompiling it, I could add them. I added 5 unique utility classes. All of them combined are used around ~120 times across all HTMLs. The longest one: @utility inputSelection { @apply w-full px-4 py-2 .5 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-100 focus : outline-none focus : ring-2 focus : ring-blue-300 focus : border-transparent transition-all appearance-none text-center [ text-align-last : center ] cursor-pointer ; } I also minified the CSS file. If I understand it, it removes anything the browser doesn’t need to understand the CSS (new lines, unnecessary spaces…). It’s better because those characters are still sent over the network, and fewer bytes mean less traffic. While using the new utility classes, I tried to preserve the layout, so everything should look the same as before.
Devlog SITE changes I found that some number inputs didn’t use the utility classes. I gave them inputText because they are really the same; the only difference is the HTML type . I also forgot to update the reloadable fuel section. It now uses the shorter utility class. Made sure that all text in the header is the same size (they weren’t). I copied all changes back to the main branch. config changes Then I added a config file using the configparser library. also added an option to disable the FastAPI docs. I started working on Wiki: reworking docs.md into a GitHub Wiki with up to date information. Error logger, so you can see what’s wrong without printing the API response in your code. I had some problems with catching the fastapi automatic response but I will leave it for next devlog
Devlog There was a small (or big) problem in my playground. If an error was returned in the response, you could only see it if your app handled it or by printing the response. That’s why I added an error logger. It can show errors in two ways: Terminal Log file How it works There were two ways of doing it (from what I learned): Middleware that captures all responses (this could be helpful in the future). Exception handlers (this is what I chose). I use two handlers: @app.exception_handler(RequestValidationError) @app.exception_handler(HTTPException) It was a horrible time searching for why it didn’t work. At first, I worked in a separate file, but I couldn’t get the (fastapi) app variable into those functions. So I copied them into main file to make them work first. I had to change some JSON responses to HTTPException to make the second handler work. After I captured the first request and printed it, I worked on one function that writes a log and another that prints to the terminal. I also created a class to store useful data. It works like this: Exception handler-> ->Put data into class -> ->Create response -> ->Return response ->Create a background task that logs the error I create a background task so the response doesn’t wait or even worse fails In the end I found that I can wrap these func with this simple func to make them work in different file def setup_error_logging(app: FastAPI): Added a config option: To disable it completely. To disable only the log file. It also bypasses all internal endpoints to make the logs more readable. The best fail… 😅 I returned an exception instead of raising it. I spent a good 15 minutes wondering why the response had status code 200 with a JSON body saying status code 400 . response changes I also checked how the authorization errors look and added two new ones: Missing Authorization header. Invalid vcc-api-key header. WIKI (not counted) I focus on making a GitHub wiki (new docs) After ending them and the error logger. Its time for the first ship !!!!!!! Nest instability: I made a backup server : https://frog01-21185.wykr.es/ Its not ideal one, bc some asian countries are blocked but its shouldn’t have any downtime (I hope so) If an Admin read it why I must copy enter from different text input 😭
Devlog Error logger added colors to the terminal print out using: \033[31m \033[0m Good to know that it is so easy to add colors to text added timestamps to log file and - between errors Changes how it reads body once again and fixed url encoded requests logging My additional information I put in detail in the json response (the real API can send it like that buttty I didn’t see it. info from docs ) additional changes: New scenarios mostly about service warnings You can add notes to the website thru config files Scopes I started working on scopes. What are they? They are really permissions (you can know them from discord bots or if you login thru GitHub etc.) I don’t want to put them in this first ship but I needed to work on something (while I work on wiki) and I CAN’T guarantee every thing works after adding them. You can see it in the scopes branch. what is done: I have made preparation so the additional database (dict) I use for OAuth can store more data have updated website func to work with changes and also pytest added all possible conve (connectivity API) scopes WIKI when I add everything to them I will finally try making a ship added the error logger page updated the sidebar scenario and snapshots page a lot of misc Bonus photos from vacation on baltic sea
Long time no see… The school started aaand I didn’t have time I had low motivation The only thing to do before first ship is docs but I could find time to finish them. Buut I back now. AND I promise to ship it before 7 of September. If you are voting on this project PLEASE do not skip. I have my birthday coming and if the shipwrights doesn’t have a long list it could mean this project payout could be my birthday gift. If I ofc ship it 😅 devlog Small marketing changes everywhere to say that this project doesn’t have any ready code to use Added info about error logger on the landing page Scopes A long way from last devlog Fixed pytest ( A second time) TEMPORARY FIX added so the scope error doesn’t - throw just internal error FIXed the Oauth2 endpoint at least 3 times Added a WIP endpoint to edit scope data from API Fixed how cars are created so they work with both - Oauth and scopes stuff Only looking dashboard page for scopes compare I think I forgot to say something in last devlog config file if you would like to use different instance Toml oillevel to oilLever AI disclaimer that I used this project to learn tailwind Make the doors have different statuses in mean time Untracked WIKI Redo the home page Started working on stardance path A lot of misc that I don’t remember other I created a small modified volvo example app to show that my app works like the real one
Devlog MAIN added a startup baner fixed snapshots not saving if website was used scope If you didn’t follow from start this branch contains the experimental scope support which is mostly done but I can guarantee stability that’s why it isn’t pushed to main in fist ship made the scope page work added a back button added button to scope on main page fixed snapshots loading/saving
Read till the end PLEASE 🥺 Devlog A lot of changes from the last devlog only 5 hrs Mostly bc I add stuff on top of but I will suffer in the future (See dev branch for these changes) Change all connectivity API to start with /connected-vehicle/v2/ to make room for different APIs After that I fixed pytest to reflect that I added a location API with a ONE endpoint There were few iterations Added scopes for this API location:read If I remember correctly Unique error responses (because location error look like old (v1) connectivity API Fixed that operationID could be a constant Added a config option to use longer API keys About here I Fixed the url for location api because it used the connectivity scheme Started working on energy API I added all data from it to this project In short one endpoints says what this car is capable of And the other says the values TODO: add its unique error its half done Scopes corresponding to energy API Fixed that some values and capabilities had the same name (bc of that some bool values where number so a it was important) WIP Oauth2 update: JWT tokens for access tokens WIP token can expire! what are JWT token? There consist of three parts header payload and signature The two first is important for my project the 3 isn’t . They store info about issuer, when token is active, scopes… A lot of useless data for this project or useful? We will see Life Bc of school I didn’t have a lot of time to write devlog (and write code) but I try to do my best before the end of stardance The next ship is planned for 28 September BIRTHDAY TODAY (16 September) Im celebrating my birthday 😄 If you write me a few good words It would be motivating for me.
Devlog A lot of breaking changes later and getting closer to PR and going to 2 ship Token lifetime WORKS made time when token is expired is rounded to int endpoint for invalidating token possibility for enabling expiring token for a specific token when it’s disabled for all in config file misc pytest for location and energy API fix the url for the enrgy API (it started with connectivity url not wi the energy api) cleanup import created new files to make the code more readable dashboard added location values added capabilities and values auto token generation and expiration in dashboard
Devlog A very long time no see. I have made a lot of progress on adding an SQL database. Testing on MySQL The backup server provider gives me access to one. SQL changes A lot of BIG changes. The reason why I haven’t posted for so long is that I didn’t want to post about something that doesn’t work. Created new classes to intercept data changes, both in case of direct dict changes and when going through the SQL model library. (I didn’t change much in the code, just imported it under the same name as the old database.) And it works! At the start, only reading data worked, but now it can send data back to the database. Added a regen function to make sure built-in API keys are always the same after a restart. Fixed the car creation function (but when I started storing the additional data in SQL, it broke AGAIN). Added configuration attributes. At first, just the database type, but then all the information needed to connect to a MySQL database. Fixed the scenario readconfig (wasn’t it updated in the past?). A really small number of things I added? Yes, but the amount of debugging and changing built-in functions in classes took me so long. (Could be because of school and not having time to do everything in one sitting.) But I learned how to make a dummy dict with a class, as I called it in the commit: proxy . Problems The SQL database can’t create new cars. Need to check all internal endpoints to make sure they work. Still haven’t merged the DEV branch (TODO). All information is stored in one column as JSON. It works, buuuut I don’t like it that way. Need to clean up the proxy classes. updating docs What failed this time? The second photo attached shows it. Have a look before reading more. _contains__ If you see it, congrats! But if you have a screen full of code, it’s harder to spot. It should be __contains__ , and I spent, I think, two days debugging why all in statements just didn’t work. I was so happy when I finally found it! XAMPP and Apple Silicon The manager app doesn’t open after a restart, so after consulting Google and AI, I found a command that I use: sudo /Applications/XAMPP/xamppfiles/xampp start xampp If someone is asking why I didn’t use the server provided one it just wasn’t practical for now.
Ships:
A Milestone Welcome everybody to the first ship of this project. What is this project? This project simulates how the Volvo API works. Why was this project created in the first place? So it’s easier to create and test your app that uses the Volvo API without using ready-made libraries. I can’t find the features you mentioned in the devlog? Check the scope branch or the upcoming dev branch. What I’m proud of: I learned a lot of things (this is my first big public project): Tailwind Python stuff (I started with C++) Writing documentation (Markdown) I also learned the basics of OAuth2 and how APIs work. Making a project with all the features needed to get started. I created an additional document for you, Stardancers: Stardance PATH What is still on the TODO list? More APIs Scopes docker image See the roadmap . What to see? error logger recreated endpoints dashboard web socket realtime changes internal endpints interactivity of the car...
|
1792853461.4565907
|
1792939861.4565907
|
|
21719
|
bs-odin by @Ankush | Stardance
|
Ankush
|
0
|
132.0
|
0
|
[]
|
[{"post_id": 31805, "ts": "202 [{"post_id": 31805, "ts": "2026-07-20T14:10:21Z", "hours": 6.0, "text": "Preformance Checkup + Audio System Things I did: I stress tested my game to work with 512 & 1024 entities. Ran under 3ms and 5ms . The difference maker was the number of entities currently in the screen (rendering). Profiled all the “slow” parts Implemented sound system , for menu and game Recorded the sounds myself!! (both in-game and ui)! Performance I tried my best to find ways to increase speed in my game. Unfortunately, all the things that I thought were “slow” weren’t actually slow. Orui UI build was like under 50micro seconds . Orui Rendering was under 250micro seconds . Animation system ran under 50micro for all entities combined iirc So, for now, there has been done no performance improvements this devlog Sound System First I made the sound system simple, for the menu. It worked and I was happy. I made 6 different similar sounds for the hover and click actions, and I play one of them randomly when clicked. menu_hover_sounds : [ 6 ] rl . Sound loadMenuSounds :: proc () { for i in 0 ..< len ( menu_hover_sounds ) { path : = fmt . ctprintf ( \"res/audio/menu/menu_click_%d.wav\" , i + 1 ) menu_click_sounds [ i ] = rl . LoadSound ( path ) } } playMenuHoveredSound :: proc () { i : = rand . int_max ( len ( menu_hover_sounds )) rl . PlaySound ( menu_hover_sounds [ i ]) } When I went on to make the Player sounds, I noticed that I am unable to the same sound twice simultaneously. I needed that so that I can simulate multiple enemies and other stuff. So I googled a bit, found this raylib example. And implemented it. For both menu and entities. TOTAL_ALIASES :: 4 Sound :: struct { aliases : [ TOTAL_ALIASES ] rl . Sound , index : int , } loadSound :: proc ( path : cstring ) -> Sound { sound : Sound sound . aliases [ 0 ] = rl . LoadSound ( path ) for i in 1 ..< TOTAL_ALIASES { sound . aliases [ i ] = rl . LoadSoundAlias ( sound . aliases [ 0 ]) } sound . index = 0 return sound } playSound :: proc ( sound : ^ Sound ) { rl . PlaySound ( sound . aliases [ sound . index ]) sound . index = ( sound . index + 1 ) % TOTAL_ALIASES } playMenuClickedSound :: proc () { i : = rand . int_max ( len ( menu_click_sounds )) playSound ( & menu_click_sounds [ i ]) } I also stole borrowed this really great background music from raylib source."}, {"post_id": 30071, "ts": "2026-07-17T11:13:28Z", "hours": 5.0, "text": "Health System, Enemy Attack, 15 new Skin! Summary of Features added: Player/Enemy Attacks Health System (Damage/Heal) Health Bar shown 15 new Skins! Avatar Menu “Set” Selector Avatar Set Selector This took a lot of time than I would like. I first had to scourge through 100s of directories for each Skin (each skin has 3 “tiers”) and get the 0th “idle” animation sprite. The next part was pretty straight forward owing to the time I took to make the architecture great :3 Health Bar This wasn’t hard as per say. But it is mad slow. I realised it earlier but never mentioned it. orui library is slow. UI libraries aren’t meant to be this slow. Probably it is due to the rendering that makes it this much slower. How slow? My renderer could render 50 entities under 1ms, now it takes ~5ms to render 20 entities. Future Todos: Performance. I will track down where orui is slow. If it is in the logic part, we can’t do anything as I am not smart enough to fix logic so complex. But if it is related to rendering, we can work with that. Performance. It’s been a while since I diagnosed the slow parts in my code. I will deep dive into the code. Find out what is slow and try to fix them The next few days, I will work on to squeeze as much performance as I can from this game."}, {"post_id": 29321, "ts": "2026-07-16T05:20:06Z", "hours": 9.0, "text": "Enemy AI done + Tons more! Other things done this time around: Fix: WASM build fixes Feature: Added the back button in Avatar Menu (finally) Fix: The character scale and position in Main/Avatar Menu acted funny in extreme dimentions (when too small) Fix: Physics renderer didn’t render polygons with 4 vertices Feature: Added physics colliders for all enemies! Feature: Y-Sorting enabled Performance: Pushed Data Oriented Design for max performance Architecture: Moved the files around, where they make more sense Feature: Implemented Attacking Feature: Player & Enemy State machine Feature: Enemy AI (super basic tbh) Performance All of these changes were made keeping performance in mind. As of right now the state machines need work to make them even faster! Obstacles State machine logic is very mind numbing, I won’t want to do that…. a lot of edge case problems. A lot of problems I never thought existed. Y-Sorting was also in the harder side because I wanted a fast sort while also not needing to change the whole array of entities. I finally decided to use another array render_list of type []int this essentially sorts the id (or index in this case) of the entities by entity.pos.y . When rendering use the ids to render. This proved to be a more efficient way to do it, than sorting the entire array of entities. Future Todos Implement Enemy attacking Implement health system (with regenability?) Draw the health bar Implement death After these things are done. We I will think of further things like game win/game end amongst many others like game pause."}, {"post_id": 28209, "ts": "2026-07-14T11:03:33Z", "hours": 5.0, "text": "Dozens of small features implemented! Migrated the End screen UI to the new UI library Brought back the physics collider with water from previously deleted code. Removed a rouge sleep call in loading phase. (Which was put there for testing ) Instead of drawing rectangles in when playing. We now draw the characters! With randomly generated skins!! Migrated Playing and loading UI to the new library Code cleanup and refactored for better architecture Culling #bd56fa When adding Culling to player rendering, I got an optimisation of 5ms->2ms. Animation while playing The initial phase was very easy. What I did struggle with was flipping the image. First, I tried flipping the texture themselves. It did work but the offset of the bones were not in place. It was fine when going right, but when I turn left. It all turns bad, the 2nd video is a demonstration of that. It took a lot of tweaks and black magic to find out that I had to also flip the anchor position and angle too 😭 Now it is working as intented :)"}, {"post_id": 26655, "ts": "2026-07-11T16:23:47Z", "hours": 4.0, "text": "Player Customisation Screen!! RAAH, this is really done! TBH this was easier than expected. I had an idea of how this will work out, and implemented just that. The menu includes some smart and fancy mathematics too // gettin the index current_index : = ( int ( curr_type ) * num_tiers ) + int ( curr_tier ) // applying the changes, Lets say user pressed right arrow. new_index : = ( current_index + 1 ) % total_options setPartType ( group , anim . CharacterType ( new_index / num_tiers )) setPartTier ( group , anim . CharacterTier ( new_index % num_tiers )) I am really proud of the UI. Looks great. Honestly I was really lucky with the great reference images I found in the internet :) hosted here"}, {"post_id": 25955, "ts": "2026-07-10T14:06:12Z", "hours": 7.0, "text": "UI Overhaul - Done! So I changed the entire UI , game UI is tough, even more so when you have a lot of moving components. I never had ever use any library like orui so it was fun, but at the same time - confusing at times. Like for example: there are only 2 components for drawing ( container and label ). I mean there’s also element, but that is just an empty container. Also I’ve tried everything but you can’t just get hover information for the container element. I need some color suggestions for the buttons in options menu, they look kinda dull… All the colors are taken from pallets at coolors.co ! Icons!! So while at it, I wanted to add icons. This was hell. A very bad idea. Let me tell you why - orui doesn’t support icons by default, so you’ve got to load another font to draw the icons. Now, Raylib can’t draw icon fonts, until you specify all the individual codepoints…here’s the code required to load a font with 3 icons: icon_codepoints : = [?] rune { 0xe048 , // ICON_LUCIDE_ARROW_LEFT 0xe049 , // ICON_LUCIDE_ARROW_RIGHT 0xe14d , // ICON_LUCIDE_SAVE 0xe18e , // ICON_LUCIDE_TRASH_2 } icon_font = rl . LoadFontEx ( \"./res/fonts/lucide.ttf\" , 32 , & icon_codepoints [ 0 ], len ( icon_codepoints ), ) Animation Blending Made the animation transition buttery smooth!! Look at the video!!"}, {"post_id": 25292, "ts": "2026-07-09T14:18:18Z", "hours": 8.0, "text": "Implemented Animation blending + UI library changed I wanted the player to be rendered in the main_menu and in the avatar menu . But also wanted the animations to be looping randomly. When I implemented random looping I found out that, the animations wouldn’t behave properly due to snapping . So I implemented animation blending . It was not at all easy , had to implement another angle_lerp function specifically for blending angles. Character customisation Due to me working hard earlier, changing character sprites is a cakewalk. In the attatched video. The head sprite is of tier 2 while the rest is from tier 1 GUI I really wanted to like raygui, but it was really bad. A lot of maths required for very simple things! I didn’t like it at all. So in search of a new UI library I found this niche library orui ! This fits my needs perfectly: Immediate mode Super fast (Not what I was looking for but a plus) Uses raylib Doesn’t depend on me to do the maths Actually customisable You can see the updated GUI in the video, looks good right :) it also supports animations and transitions!"}, {"post_id": 24692, "ts": "2026-07-08T13:28:51Z", "hours": 11.0, "text": "Fully functional animation parser and engine! The entire thing started by me searching for assets to use for my game, then I found these super cute chibi characters that too FOR FREE!! I downloaded one of the free ones and saw that they use .scml file for animations. They do have pre-rendered png images in sequence too, but that is too easy, not fun and doesn’t look good! So, I planned to make a .scml parser, my own animation engine. And see how it goes. In the added video there’s the working part and the non-working part, which I found pretty funny. 1. Image loader First I made a image loader that dynamically loads all the sprites that are in use. Later I will add the abitily to drop the sprites from memory when not in use. 2. Mapping the data This was, tough. Very, very tough that I had originally anticipated. Making this parser included me staring at a file with 9k lines for hours. Trying to figure out what each elements do and how to use them interconnectedly. I first made a scml_data.odin where I one by one mapped all the required data to odin structs. 3. .scml parser I created a new file scml_parser.odin and in it I loaded the .scml file with Odin’s built in xml parser. The parsing took a big chunk of time too, as there are not good docs or examples available out here. But there was one single forum post that got me going and I never had any problems thereafter. I parse the file and save the values in my global private data variable defined in scml_data.odin . I read each elements and added all the necessary ones. I observed all the attributes, data and values very carefully earlier so I had an idea of some repetitive values that I could skip. I think almost 50% of the file was repetition, which ofc I didn’t save. 4. The engine Now that I had all the data with me in, the only thing I had to do was use that. With a given time t I have to find the timeline that it lands on and interpolate the time, linearly lerp the values and issue a draw command for the single sprite. I was unable to do two specific things and had to take help from AI: Lerping Angle: angle lerping is different as there can be a case where we have to learp from 350 to 10, normal lerp functions would just lerp it 350->200->10 instead of 350->360->10 Using the parent bones to calculate the final position of the child node. The particular function ai generated is here . Debugging After making the engine, we got to fix the engine. It had bugs, I debugged for a long time and then fixed them. Pretty stupid things like not pushing the timeline keys after initialising them, flipped y-axis, and angle clockwise order. Final thoughts Finally the thing is working now, I still have to polish it and make it play nicely with my entire game. There are other optimisation opportunities available but I’ll put that for later. I will now work on the avatar menu, so user can mix different things."}, {"post_id": 23439, "ts": "2026-07-06T12:29:30Z", "hours": 6.0, "text": "Nothing much this time. Added options menu Added physics collider to the player (finally, phew..) Notify mobile users to play in landscape mode. That’s about it. All of them were relatively easy. What I did try and haven’t pushed to upstream is a failed attempt to detect entry and exit from water bodies by the entities in an economical way."}, {"post_id": 22204, "ts": "2026-07-04T15:44:46Z", "hours": 6.0, "text": "Drops and drops together make an ocean. Did nothing major just some small stuff Added buttons (RayGUI) for navigation #7608e9 Added Loading Screen before match starts. #76c086 , #26ddc4 Made the game fully self-contained. The game executable requires no dlls, no other libraries or assets either. Everything is bundled together in a single executable! For this to work, I used #load(\"file_source\") function. It returns []u8 or in C terms *uint8_t . But this won’t work with the raygui style loader function rl.GuiLoadStyle() that requires the ‘filename’ and not the data. So for that reason. I read the source code of raygui an d implemented their function in Odin since they don’t expose the function to the user. That’s about it tho!"}, {"post_id": 20936, "ts": "2026-07-02T17:49:13Z", "hours": 8.0, "text": "From bare metals to web. A rough road. My game is now available to play in bs-odin.onkush.dev I built my application for the Web and it works. Probably the most dirty work I had to do in this project overall. I probably have read all available Odin / WASM templates. Setting the Sail I knew what WASM is, I once to compile one of my C++ games to WASM too, but seeing more than 500+ errors in the first try took a big toll on me and I didn’t bother. I don’t want this project to end up like that. I didn’t have to search much to find a template that builds Odin + raylib in WASM . So I started to implement ‘ Offline Mode ’ for my game. Didn’t take much time tbh, easy work. So I went to sleep and assigned WASM implementation to future me. Thunderstorm Next morning, I looked at how the {template]( https://github.com/karl-zylinski/odin-raylib-web/ ) works - really simple and straightforward. I copied their code with proper attribution ofc. After fixing some build bugs, I ran my build.sh and…It didn’t work. But nothing works the first time… Spoiler, it didn’t work in the 69th time either. Tsunami By evening, I was able to make it compile, I won’t get into the specifics but it had to do with multiple definition of functions within Odin core libraries. So, after it compiled. I had a huge sigh of relief, I ran it in my browser and bam! A fully dark screen! Wait…What? NOO I opened up the console… and Alas! Errors, not 500 errors, only one error Uncaught (in promise) InternalError: too much recursion I couldn’t find what caused this error, how to fix it, how to narrow it down. Lastly I gave up and went to sleep. After I woke up! I one by one tested each file and their dependency. Lastly I found the culprit…it was Box2D After I dug deeper I found the fix, but it was concerned with changing the source code of Odin…so virtually no one can compile my game to WASM but me… I’ll try to find a workaround for it, till then - if it works, it works. The arrival to the forbidden Island After a lot of hardwork, I got the fruits of my labour. This journey was not as much fun, but seeing my game run in my browser is so much worth it! I fixed few visual glitches and frame time capture fixes. And then I pushed it into my domain :)"}, {"post_id": 19190, "ts": "2026-06-30T13:37:42Z", "hours": 10.0, "text": "Implemented Physics, generated islands form the terrain + Server side Seed generation. Performance Performance is out of the window, the algorithm that generates the islands is O(n^3) !! This disgusts me, but I wasn’t even able to come up with this solution! AI made the entire function…unfortunately I was not able to come up with the solution by myself… I understand how the function works, but I don’t know how I could improve it anyway. The one upside to this is that, this function will only run once - during the game start. My beloved physics engine Box2D can easily handle all these complex islands with no problem at all! So that helps :) Time What took 10hrs? Most of the time was trying to generate the islands, another huge chunk of time was spent making the renderer for Box2D #510535 . IDK how, but the server side seed generation that I though would take less time took about 2hrs… #4bac51 My state of mind I am currently disappointed in myself, multiplayer is hard. Server side seed generation taking so much time destroyed my confidence. Not being able to generate the islands broke me… I am having doubts of being competent enough to complete this project. My current plan is to focus on single player - offline mode. I will handle multiplayer with server later. Currently I will focus on things that are fun and actually have meaning. First I’ll make it work fully in web, then I will do character…"}, {"post_id": 17458, "ts": "2026-06-28T07:42:10Z", "hours": 5.0, "text": "Frame Time so low, we’re drawing the future. Last devlog I said Raylib was slow. It is on me, I am dumb. I didn’t have to look more into it, DrawMesh did the exact thing I required! Currently, the frame renders in my laptop under 0.9ms instead of 3-4ms earlier and in this devlog I document the changes I made. Batch Rendering Earlier I iterated over all the vertices and rendered them. for v in vertices { rlgl . Color4ub ( v . color . r , v . color . g , v . color . b , v . color . a ) rlgl . Vertex2f ( v . pos . x , v . pos . y ) } That was very slow, when I profiled it I saw that just iterating over the vertices took about 1.4ms . Drawing took another 1.5ms . So I changed the vertices data: // old vertex :: struct { pos : rl . Vector2 , color : rl . Color , } vertices : [ dynamic ] vertex // new vertices_pos : [ dynamic ] rl . Vector3 vertices_col : [ dynamic ] rl . Color Then I generate a terrain_mesh using these arrays in this function. And use that to render everything at once! if mesh_initialised && terrain_mesh . vaoId != 0 { rl . DrawMesh ( terrain_mesh , default_material , default_transform ) } Results Went from 3-4ms to 0.5ms . But when we move we have to generate the vertices at each frame! So if we don’ t move it is 0.5ms but if we move it is 4.0+ms !!! Chunking Since our terrain doesn’t change, we can store the results in GPU memory and render them at will. But if we try to render a texture of size 5120x5120 , our gpu will suffer really bad. So we chunk. We divide the terrain in chunks, load them into gpu and show only the parts that are visible currently. Sounds easy. But for stupid ppl like me it is hard, took me a long while to fight the memory, logic and silly mistakes. But finally, it did work. Results It renders about 0.8ms avg and 1ms in high load, sometimes goes down to 0.6ms too! That too while moving!"}, {"post_id": 16739, "ts": "2026-06-27T06:14:54Z", "hours": 6.0, "text": "Reworked my entire Rendering and Windowing System… Raylib - The unfortunate switch I wanted to make a game accessible to many people. And the most accessible platform is Web. So I had to compile my application in WASM . While my previous renderer, SDL_Renderer was able to render in Web it was not meant to be used intensively, it doesn’t support shaders among many other flaws. So I looked for other rendering libraries to use for my game. bgfx - no bindings for Odin sokol - I never used it, I had to ask myself if the complexity it brings with it is worth it. Very sweet option otherwise. Vulkan - No WASM SDL_GPU - No WASM Raylib comes with it’s own downsides tho - It is slow, just changing to it added 2ms to my render time, it was 1ms with SDL Renderer. But it’s simplicity keeps me in awe. Right now I’ve decided to settle with my slower Raylib and focus on important stuff like offline mode , bots , characters ."}, {"post_id": 15624, "ts": "2026-06-25T12:36:30Z", "hours": 7.0, "text": "A massive overhaul of game architecture. Architecture I think it’s safe to say that this time I unscrambled the spaghetti code that I have been writing. These things are hard but very important for a project of this scale. Else, the developer burden piles up and motivation to complete the game is dead. #883172 . Performance I implemented a simple performance hack to my terrain generation. This fix gives almost 100% faster terrain generation and rendering! I’ve documented the fix in the comments of the code here . #f2a07a Camera Till now the camera didn’t follow the player, but now it does. The math again, wasn’t easy. I introduced another bug that I caught days later. #393da8 Others Added a feature to ping server at any moment #da64b7 Enforced naming convention Out of bounds array access fix"}, {"post_id": 14192, "ts": "2026-06-23T05:49:53Z", "hours": 3.0, "text": "Implemented Marching Squares! Brief intro to marching squares This is a technique commonly used in 2d terrain generation models. It converts the rigid ‘blocky’ tiling of squares to smth more organic by adding triangles to the mix. The end result is even more good looking when you add linear interpolation! The implementation Took everything from me. I feel exhausted and tired, I need sleep. The implementation took a lot of hand written logic, here and here . But idk how but the lookup table was perfect, and required no changes! Sure took some time to make it but the results speak for themselves I think. What was indeed the problem… this code in github _bl : = bl > threshold ? 0b0010 : 0 _br : = br > threshold ? 0b0001 : 0 The fix? _bl : = bl > threshold ? 0b0001 : 0 _br : = br > threshold ? 0b0010 : 0 I had these two swapped from what the standard says! Oh my gawd, this took forever to debug. Performance The current implementation is using Painter's Algorithm , this works but there is a heck ton of overdraw! I hate it and probably will try to find a fix to it soon!"}, {"post_id": 13224, "ts": "2026-06-21T17:36:10Z", "hours": 3.0, "text": "Made rendering more than 4000% faster ! Premature optimisation is the root… So initially my renderer drew all the cells, even when not in view. There were only 64x64 cells so it was no problem for my lappy at all! But when I increased it to 512x512 my laptop took huge load (45ms rendering time)! Currently after implementing a camera thingy it is now not an issue. The terrain is generated once and only the visible part (80x45 cells) is rendered in the screen! This change lead to render times go below 1ms again :) The maths behind camera thing was really logical error prone, and took a lot of time + I had a rough time working with dynamic arrays of Odin. Apparently you got to use raw_data(array) function to get the underlying data from the array. This is not documented anywhere neither did LSP find me this function when I did try to find it! Other stuff I also changed the colours of the terrain. Added background to the fps and frame_time texts"}, {"post_id": 12636, "ts": "2026-06-20T17:10:25Z", "hours": 5.0, "text": "Procedural Terrain Generation is working now! Maths I kinda learnt a lot of maths for Perlin and simplex noise generations! Watched a lot of tutorials to get an idea of how it is generally implemented to generate terrain. It is very easy when you don’t have to code the noise functions yourselves… Resources: 1 , 2 Opensource helping while at it I also added imgui as a dependency. When imgui was building I saw a two big libraries (the actual imgui and sdl3 repos) clone. They were more than 100mb each. I made some simple code changes build.py of the original repo which brought it down to 1-2mb each. I made an issue in the original bindings repo. Let’s hope it goes well. Rendering I changed the rendering technique of the tiles of the generated terrain twice. It was real hard work. This probably took most of the time. There were many errors in maths and logic during the making of terrain renderer."}, {"post_id": 11783, "ts": "2026-06-19T05:28:23Z", "hours": 18.0, "text": "Multiplayer setup done! Server and client work flawlessly! This devlog is bigger than my normal work, because I started this project not knowing about Stardance tldr; This is a 9 day blog of my game I am making from scratch using odin lang, sdl3 for window and rendering(will switch to vulkan later on), and ENet for networking. During the 18hrs and 9 days I made functional multiplayer, rendering, game states, input handling and profiling. I spent a lot my time in the architecture and infra, in the later days, I will focus on features itself. When done, I will improve architecture and performance I spent more than 2-3 hours writing and editing the devlog, but it became too long and stardance website didn’t allow me to post it here (stardance has a limit of 4k characters and my devlog was about 6k ). So I had to upload it in my own site here . I am so sorry for the inconveniences caused 🙏"}]...
|
Title: bs-odin by @Ankush | Stardance
Devlogs:
Mu Title: bs-odin by @Ankush | Stardance
Devlogs:
Multiplayer setup done! Server and client work flawlessly! This devlog is bigger than my normal work, because I started this project not knowing about Stardance tldr; This is a 9 day blog of my game I am making from scratch using odin lang, sdl3 for window and rendering(will switch to vulkan later on), and ENet for networking. During the 18hrs and 9 days I made functional multiplayer, rendering, game states, input handling and profiling. I spent a lot my time in the architecture and infra, in the later days, I will focus on features itself. When done, I will improve architecture and performance I spent more than 2-3 hours writing and editing the devlog, but it became too long and stardance website didn’t allow me to post it here (stardance has a limit of 4k characters and my devlog was about 6k ). So I had to upload it in my own site here . I am so sorry for the inconveniences caused 🙏
Procedural Terrain Generation is working now! Maths I kinda learnt a lot of maths for Perlin and simplex noise generations! Watched a lot of tutorials to get an idea of how it is generally implemented to generate terrain. It is very easy when you don’t have to code the noise functions yourselves… Resources: 1 , 2 Opensource helping while at it I also added imgui as a dependency. When imgui was building I saw a two big libraries (the actual imgui and sdl3 repos) clone. They were more than 100mb each. I made some simple code changes build.py of the original repo which brought it down to 1-2mb each. I made an issue in the original bindings repo. Let’s hope it goes well. Rendering I changed the rendering technique of the tiles of the generated terrain twice. It was real hard work. This probably took most of the time. There were many errors in maths and logic during the making of terrain renderer.
Made rendering more than 4000% faster ! Premature optimisation is the root… So initially my renderer drew all the cells, even when not in view. There were only 64x64 cells so it was no problem for my lappy at all! But when I increased it to 512x512 my laptop took huge load (45ms rendering time)! Currently after implementing a camera thingy it is now not an issue. The terrain is generated once and only the visible part (80x45 cells) is rendered in the screen! This change lead to render times go below 1ms again :) The maths behind camera thing was really logical error prone, and took a lot of time + I had a rough time working with dynamic arrays of Odin. Apparently you got to use raw_data(array) function to get the underlying data from the array. This is not documented anywhere neither did LSP find me this function when I did try to find it! Other stuff I also changed the colours of the terrain. Added background to the fps and frame_time texts
Implemented Marching Squares! Brief intro to marching squares This is a technique commonly used in 2d terrain generation models. It converts the rigid ‘blocky’ tiling of squares to smth more organic by adding triangles to the mix. The end result is even more good looking when you add linear interpolation! The implementation Took everything from me. I feel exhausted and tired, I need sleep. The implementation took a lot of hand written logic, here and here . But idk how but the lookup table was perfect, and required no changes! Sure took some time to make it but the results speak for themselves I think. What was indeed the problem… this code in github _bl : = bl > threshold ? 0b0010 : 0 _br : = br > threshold ? 0b0001 : 0 The fix? _bl : = bl > threshold ? 0b0001 : 0 _br : = br > threshold ? 0b0010 : 0 I had these two swapped from what the standard says! Oh my gawd, this took forever to debug. Performance The current implementation is using Painter's Algorithm , this works but there is a heck ton of overdraw! I hate it and probably will try to find a fix to it soon!
A massive overhaul of game architecture. Architecture I think it’s safe to say that this time I unscrambled the spaghetti code that I have been writing. These things are hard but very important for a project of this scale. Else, the developer burden piles up and motivation to complete the game is dead. #883172 . Performance I implemented a simple performance hack to my terrain generation. This fix gives almost 100% faster terrain generation and rendering! I’ve documented the fix in the comments of the code here . #f2a07a Camera Till now the camera didn’t follow the player, but now it does. The math again, wasn’t easy. I introduced another bug that I caught days later. #393da8 Others Added a feature to ping server at any moment #da64b7 Enforced naming convention Out of bounds array access fix
Reworked my entire Rendering and Windowing System… Raylib - The unfortunate switch I wanted to make a game accessible to many people. And the most accessible platform is Web. So I had to compile my application in WASM . While my previous renderer, SDL_Renderer was able to render in Web it was not meant to be used intensively, it doesn’t support shaders among many other flaws. So I looked for other rendering libraries to use for my game. bgfx - no bindings for Odin sokol - I never used it, I had to ask myself if the complexity it brings with it is worth it. Very sweet option otherwise. Vulkan - No WASM SDL_GPU - No WASM Raylib comes with it’s own downsides tho - It is slow, just changing to it added 2ms to my render time, it was 1ms with SDL Renderer. But it’s simplicity keeps me in awe. Right now I’ve decided to settle with my slower Raylib and focus on important stuff like offline mode , bots , characters .
Frame Time so low, we’re drawing the future. Last devlog I said Raylib was slow. It is on me, I am dumb. I didn’t have to look more into it, DrawMesh did the exact thing I required! Currently, the frame renders in my laptop under 0.9ms instead of 3-4ms earlier and in this devlog I document the changes I made. Batch Rendering Earlier I iterated over all the vertices and rendered them. for v in vertices { rlgl . Color4ub ( v . color . r , v . color . g , v . color . b , v . color . a ) rlgl . Vertex2f ( v . pos . x , v . pos . y ) } That was very slow, when I profiled it I saw that just iterating over the vertices took about 1.4ms . Drawing took another 1.5ms . So I changed the vertices data: // old vertex :: struct { pos : rl . Vector2 , color : rl . Color , } vertices : [ dynamic ] vertex // new vertices_pos : [ dynamic ] rl . Vector3 vertices_col : [ dynamic ] rl . Color Then I generate a terrain_mesh using these arrays in this function. And use that to render everything at once! if mesh_initialised && terrain_mesh . vaoId != 0 { rl . DrawMesh ( terrain_mesh , default_material , default_transform ) } Results Went from 3-4ms to 0.5ms . But when we move we have to generate the vertices at each frame! So if we don’ t move it is 0.5ms but if we move it is 4.0+ms !!! Chunking Since our terrain doesn’t change, we can store the results in GPU memory and render them at will. But if we try to render a texture of size 5120x5120 , our gpu will suffer really bad. So we chunk. We divide the terrain in chunks, load them into gpu and show only the parts that are visible currently. Sounds easy. But for stupid ppl like me it is hard, took me a long while to fight the memory, logic and silly mistakes. But finally, it did work. Results It renders about 0.8ms avg and 1ms in high load, sometimes goes down to 0.6ms too! That too while moving!
Implemented Physics, generated islands form the terrain + Server side Seed generation. Performance Performance is out of the window, the algorithm that generates the islands is O(n^3) !! This disgusts me, but I wasn’t even able to come up with this solution! AI made the entire function…unfortunately I was not able to come up with the solution by myself… I understand how the function works, but I don’t know how I could improve it anyway. The one upside to this is that, this function will only run once - during the game start. My beloved physics engine Box2D can easily handle all these complex islands with no problem at all! So that helps :) Time What took 10hrs? Most of the time was trying to generate the islands, another huge chunk of time was spent making the renderer for Box2D #510535 . IDK how, but the server side seed generation that I though would take less time took about 2hrs… #4bac51 My state of mind I am currently disappointed in myself, multiplayer is hard. Server side seed generation taking so much time destroyed my confidence. Not being able to generate the islands broke me… I am having doubts of being competent enough to complete this project. My current plan is to focus on single player - offline mode. I will handle multiplayer with server later. Currently I will focus on things that are fun and actually have meaning. First I’ll make it work fully in web, then I will do character…
From bare metals to web. A rough road. My game is now available to play in bs-odin.onkush.dev I built my application for the Web and it works. Probably the most dirty work I had to do in this project overall. I probably have read all available Odin / WASM templates. Setting the Sail I knew what WASM is, I once to compile one of my C++ games to WASM too, but seeing more than 500+ errors in the first try took a big toll on me and I didn’t bother. I don’t want this project to end up like that. I didn’t have to search much to find a template that builds Odin + raylib in WASM . So I started to implement ‘ Offline Mode ’ for my game. Didn’t take much time tbh, easy work. So I went to sleep and assigned WASM implementation to future me. Thunderstorm Next morning, I looked at how the {template]( https://github.com/karl-zylinski/odin-raylib-web/ ) works - really simple and straightforward. I copied their code with proper attribution ofc. After fixing some build bugs, I ran my build.sh and…It didn’t work. But nothing works the first time… Spoiler, it didn’t work in the 69th time either. Tsunami By evening, I was able to make it compile, I won’t get into the specifics but it had to do with multiple definition of functions within Odin core libraries. So, after it compiled. I had a huge sigh of relief, I ran it in my browser and bam! A fully dark screen! Wait…What? NOO I opened up the console… and Alas! Errors, not 500 errors, only one error Uncaught (in promise) InternalError: too much recursion I couldn’t find what caused this error, how to fix it, how to narrow it down. Lastly I gave up and went to sleep. After I woke up! I one by one tested each file and their dependency. Lastly I found the culprit…it was Box2D After I dug deeper I found the fix, but it was concerned with changing the source code of Odin…so virtually no one can compile my game to WASM but me… I’ll try to find a workaround for it, till then - if it works, it works. The arrival to the forbidden Island After a lot of hardwork, I got the fruits of my labour. This journey was not as much fun, but seeing my game run in my browser is so much worth it! I fixed few visual glitches and frame time capture fixes. And then I pushed it into my domain :)
Drops and drops together make an ocean. Did nothing major just some small stuff Added buttons (RayGUI) for navigation #7608e9 Added Loading Screen before match starts. #76c086 , #26ddc4 Made the game fully self-contained. The game executable requires no dlls, no other libraries or assets either. Everything is bundled together in a single executable! For this to work, I used #load("file_source") function. It returns []u8 or in C terms *uint8_t . But this won’t work with the raygui style loader function rl.GuiLoadStyle() that requires the ‘filename’ and not the data. So for that reason. I read the source code of raygui an d implemented their function in Odin since they don’t expose the function to the user. That’s about it tho!
Nothing much this time. Added options menu Added physics collider to the player (finally, phew..) Notify mobile users to play in landscape mode. That’s about it. All of them were relatively easy. What I did try and haven’t pushed to upstream is a failed attempt to detect entry and exit from water bodies by the entities in an economical way.
Fully functional animation parser and engine! The entire thing started by me searching for assets to use for my game, then I found these super cute chibi characters that too FOR FREE!! I downloaded one of the free ones and saw that they use .scml file for animations. They do have pre-rendered png images in sequence too, but that is too easy, not fun and doesn’t look good! So, I planned to make a .scml parser, my own animation engine. And see how it goes. In the added video there’s the working part and the non-working part, which I found pretty funny. 1. Image loader First I made a image loader that dynamically loads all the sprites that are in use. Later I will add the abitily to drop the sprites from memory when not in use. 2. Mapping the data This was, tough. Very, very tough that I had originally anticipated. Making this parser included me staring at a file with 9k lines for hours. Trying to figure out what each elements do and how to use them interconnectedly. I first made a scml_data.odin where I one by one mapped all the required data to odin structs. 3. .scml parser I created a new file scml_parser.odin and in it I loaded the .scml file with Odin’s built in xml parser. The parsing took a big chunk of time too, as there are not good docs or examples available out here. But there was one single forum post that got me going and I never had any problems thereafter. I parse the file and save the values in my global private data variable defined in scml_data.odin . I read each elements and added all the necessary ones. I observed all the attributes, data and values very carefully earlier so I had an idea of some repetitive values that I could skip. I think almost 50% of the file was repetition, which ofc I didn’t save. 4. The engine Now that I had all the data with me in, the only thing I had to do was use that. With a given time t I have to find the timeline that it lands on and interpolate the time, linearly lerp the values and issue a draw command for the single sprite. I was unable to do two specific things and had to take help from AI: Lerping Angle: angle lerping is different as there can be a case where we have to learp from 350 to 10, normal lerp functions would just lerp it 350->200->10 instead of 350->360->10 Using the parent bones to calculate the final position of the child node. The particular function ai generated is here . Debugging After making the engine, we got to fix the engine. It had bugs, I debugged for a long time and then fixed them. Pretty stupid things like not pushing the timeline keys after initialising them, flipped y-axis, and angle clockwise order. Final thoughts Finally the thing is working now, I still have to polish it and make it play nicely with my entire game. There are other optimisation opportunities available but I’ll put that for later. I will now work on the avatar menu, so user can mix different things.
Implemented Animation blending + UI library changed I wanted the player to be rendered in the main_menu and in the avatar menu . But also wanted the animations to be looping randomly. When I implemented random looping I found out that, the animations wouldn’t behave properly due to snapping . So I implemented animation blending . It was not at all easy , had to implement another angle_lerp function specifically for blending angles. Character customisation Due to me working hard earlier, changing character sprites is a cakewalk. In the attatched video. The head sprite is of tier 2 while the rest is from tier 1 GUI I really wanted to like raygui, but it was really bad. A lot of maths required for very simple things! I didn’t like it at all. So in search of a new UI library I found this niche library orui ! This fits my needs perfectly: Immediate mode Super fast (Not what I was looking for but a plus) Uses raylib Doesn’t depend on me to do the maths Actually customisable You can see the updated GUI in the video, looks good right :) it also supports animations and transitions!
UI Overhaul - Done! So I changed the entire UI , game UI is tough, even more so when you have a lot of moving components. I never had ever use any library like orui so it was fun, but at the same time - confusing at times. Like for example: there are only 2 components for drawing ( container and label ). I mean there’s also element, but that is just an empty container. Also I’ve tried everything but you can’t just get hover information for the container element. I need some color suggestions for the buttons in options menu, they look kinda dull… All the colors are taken from pallets at coolors.co ! Icons!! So while at it, I wanted to add icons. This was hell. A very bad idea. Let me tell you why - orui doesn’t support icons by default, so you’ve got to load another font to draw the icons. Now, Raylib can’t draw icon fonts, until you specify all the individual codepoints…here’s the code required to load a font with 3 icons: icon_codepoints : = [?] rune { 0xe048 , // ICON_LUCIDE_ARROW_LEFT 0xe049 , // ICON_LUCIDE_ARROW_RIGHT 0xe14d , // ICON_LUCIDE_SAVE 0xe18e , // ICON_LUCIDE_TRASH_2 } icon_font = rl . LoadFontEx ( "./res/fonts/lucide.ttf" , 32 , & icon_codepoints [ 0 ], len ( icon_codepoints ), ) Animation Blending Made the animation transition buttery smooth!! Look at the video!!
Player Customisation Screen!! RAAH, this is really done! TBH this was easier than expected. I had an idea of how this will work out, and implemented just that. The menu includes some smart and fancy mathematics too // gettin the index current_index : = ( int ( curr_type ) * num_tiers ) + int ( curr_tier ) // applying the changes, Lets say user pressed right arrow. new_index : = ( current_index + 1 ) % total_options setPartType ( group , anim . CharacterType ( new_index / num_tiers )) setPartTier ( group , anim . CharacterTier ( new_index % num_tiers )) I am really proud of the UI. Looks great. Honestly I was really lucky with the great reference images I found in the internet :) hosted here
Dozens of small features implemented! Migrated the End screen UI to the new UI library Brought back the physics collider with water from previously deleted code. Removed a rouge sleep call in loading phase. (Which was put there for testing ) Instead of drawing rectangles in when playing. We now draw the characters! With randomly generated skins!! Migrated Playing and loading UI to the new library Code cleanup and refactored for better architecture Culling #bd56fa When adding Culling to player rendering, I got an optimisation of 5ms->2ms. Animation while playing The initial phase was very easy. What I did struggle with was flipping the image. First, I tried flipping the texture themselves. It did work but the offset of the bones were not in place. It was fine when going right, but when I turn left. It all turns bad, the 2nd video is a demonstration of that. It took a lot of tweaks and black magic to find out that I had to also flip the anchor position and angle too 😭 Now it is working as intented :)
Enemy AI done + Tons more! Other things done this time around: Fix: WASM build fixes Feature: Added the back button in Avatar Menu (finally) Fix: The character scale and position in Main/Avatar Menu acted funny in extreme dimentions (when too small) Fix: Physics renderer didn’t render polygons with 4 vertices Feature: Added physics colliders for all enemies! Feature: Y-Sorting enabled Performance: Pushed Data Oriented Design for max performance Architecture: Moved the files around, where they make more sense Feature: Implemented Attacking Feature: Player & Enemy State machine Feature: Enemy AI (super basic tbh) Performance All of these changes were made keeping performance in mind. As of right now the state machines need work to make them even faster! Obstacles State machine logic is very mind numbing, I won’t want to do that…. a lot of edge case problems. A lot of problems I never thought existed. Y-Sorting was also in the harder side because I wanted a fast sort while also not needing to change the whole array of entities. I finally decided to use another array render_list of type []int this essentially sorts the id (or index in this case) of the entities by entity.pos.y . When rendering use the ids to render. This proved to be a more efficient way to do it, than sorting the entire array of entities. Future Todos Implement Enemy attacking Implement health system (with regenability?) Draw the health bar Implement death After these things are done. We I will think of further things like game win/game end amongst many others like game pause.
Health System, Enemy Attack, 15 new Skin! Summary of Features added: Player/Enemy Attacks Health System (Damage/Heal) Health Bar shown 15 new Skins! Avatar Menu “Set” Selector Avatar Set Selector This took a lot of time than I would like. I first had to scourge through 100s of directories for each Skin (each skin has 3 “tiers”) and get the 0th “idle” animation sprite. The next part was pretty straight forward owing to the time I took to make the architecture great :3 Health Bar This wasn’t hard as per say. But it is mad slow. I realised it earlier but never mentioned it. orui library is slow. UI libraries aren’t meant to be this slow. Probably it is due to the rendering that makes it this much slower. How slow? My renderer could render 50 entities under 1ms, now it takes ~5ms to render 20 entities. Future Todos: Performance. I will track down where orui is slow. If it is in the logic part, we can’t do anything as I am not smart enough to fix logic so complex. But if it is related to rendering, we can work with that. Performance. It’s been a while since I diagnosed the slow parts in my code. I will deep dive into the code. Find out what is slow and try to fix them The next few days, I will work on to squeeze as much performance as I can from this game.
Preformance Checkup + Audio System Things I did: I stress tested my game to work with 512 & 1024 entities. Ran under 3ms and 5ms . The difference maker was the number of entities currently in the screen (rendering). Profiled all the “slow” parts Implemented sound system , for menu and game Recorded the sounds myself!! (both in-game and ui)! Performance I tried my best to find ways to increase speed in my game. Unfortunately, all the things that I thought were “slow” weren’t actually slow. Orui UI build was like under 50micro seconds . Orui Rendering was under 250micro seconds . Animation system ran under 50micro for all entities combined iirc So, for now, there has been done no performance improvements this devlog Sound System First I made the sound system simple, for the menu. It worked and I was happy. I made 6 different similar sounds for the hover and click actions, and I play one of them randomly when clicked. menu_hover_sounds : [ 6 ] rl . Sound loadMenuSounds :: proc () { for i in 0 ..< len ( menu_hover_sounds ) { path : = fmt . ctprintf ( "res/audio/menu/menu_click_%d.wav" , i + 1 ) menu_click_sounds [ i ] = rl . LoadSound ( path ) } } playMenuHoveredSound :: proc () { i : = rand . int_max ( len ( menu_hover_sounds )) rl . PlaySound ( menu_hover_sounds [ i ]) } When I went on to make the Player sounds, I noticed that I am unable to the same sound twice simultaneously. I needed that so that I can simulate multiple enemies and other stuff. So I googled a bit, found this raylib example. And implemented it. For both menu and entities. TOTAL_ALIASES :: 4 Sound :: struct { aliases : [ TOTAL_ALIASES ] rl . Sound , index : int , } loadSound :: proc ( path : cstring ) -> Sound { sound : Sound sound . aliases [ 0 ] = rl . LoadSound ( path ) for i in 1 ..< TOTAL_ALIASES { sound . aliases [ i ] = rl . LoadSoundAlias ( sound . aliases [ 0 ]) } sound . index = 0 return sound } playSound :: proc ( sound : ^ Sound ) { rl . PlaySound ( sound . aliases [ sound . index ]) sound . index = ( sound . index + 1 ) % TOTAL_ALIASES } playMenuClickedSound :: proc () { i : = rand . int_max ( len ( menu_click_sounds )) playSound ( & menu_click_sounds [ i ]) } I also stole borrowed this really great background music from raylib source....
|
1791826431.9220161
|
1792690431.9220161
|
|
41548
|
Market Lab by @GoldenClarinet | Stardance
|
GoldenClarinet
|
1
|
132.14
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-20T13:44:17Z", "hours": 130.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "MarketLab is a python tool to exploring BINANCE financial data. With MarketLab, you can create and manage database of market data such as the OHLCV, calculated indicators such as SMA, RSI etc… The most challenging part was to rebuilt the core of the project. I am very proud of MarketLab because it is my very first and my most complex python project yet.", "repo": "https://github.com/Cla1tra2algo/Market-Lab", "demo": "https://pypi.org/project/the-marketlab-project/"}]...
|
[{"post_id": 74089, "ts": "202 [{"post_id": 74089, "ts": "2026-09-26T17:22:31Z", "hours": 0.6122, "text": "After Ship DevLog #2: I changed manually the README to reduce the part of AI code"}, {"post_id": 72266, "ts": "2026-09-22T15:30:09Z", "hours": 1.5233, "text": "After DevLog #1 : I have edited parts of the README and fixed a bug when window selection. Thanks for reading 😄"}, {"post_id": 71199, "ts": "2026-09-20T13:09:32Z", "hours": 6.0994, "text": "Final DevLog (#33) : Great Great Great New !! The first version of MarketLab is finished !! You can now install it, if you have python with a simple pip install the-marketlab-project and import it under the name of marketlab ! Thanks for reading ad for all the support you gave me 😄 ✌️ ! Special thanks to @dapsvi , @jinjin , @preyas_21 and @MathBty for liking DevLog #33 !"}, {"post_id": 64930, "ts": "2026-09-09T17:27:42Z", "hours": 1.0522, "text": "DevLog #32 : Newfeature ! : I finally fixed the bug that misplaced and made calcultation over wrong data to update indicators. Thes next step is to add the update for the events. Thanks for reading 😄 PS : Special thanks to @kingkev for liking DevLog #31 and following and thank to @No1Michael for liking DevLog #30 !"}, {"post_id": 63039, "ts": "2026-09-06T16:40:05Z", "hours": 0.3269, "text": "DevLog #31 : New feature (not perfect yet) ! I am fixing the issues on the indicator update (see DevLog #30). The indicator is now updated over te whole set of new data and a misplacement has been corrected. But some problems remains: the indicator is updated with what seems to be the first value of the whole set of data and it is also calculated with the very first values (that normally cannot be done: if you are calculating a SMA100, the 99 firsts values cannot be calculated) Thanks for reading 😄"}, {"post_id": 62995, "ts": "2026-09-06T15:44:46Z", "hours": 1.2817, "text": "DevLog #30 : New feature (not perfect yet) ! You can now update the calculated indicators, but an issue just showed up: there are three misplacement of the new values of the indicator. I am working on fixing that. Thanks for reading 🫡 PS : Special thanks to @bali0531 , @lakshaykhatri780 and @jinjin for liking DevLog #29 !"}, {"post_id": 62088, "ts": "2026-09-05T10:32:14Z", "hours": 4.7581, "text": "DevLog #29 : New feature ! You can now update the data available on Binance API. I am also working on calculating indicators and avents over these new data. Thanks for reading 😄 PS: If you know how the 3D Printing Credits available in the shop can be used, I will be grateful if you tell me in the comments!"}, {"post_id": 58871, "ts": "2026-08-31T13:24:47Z", "hours": 8.6258, "text": "DevLog #28 : I finalized the sync of all the part of the system that manage events calculation with the new core of MarketLab. I also started the README. I am currently working on updating all the data after a break in your work on MarketLab. Thanks for reading ✌️! PS: Special thanks again to @bali0531 for liking DevLog #27 ! PS 2: I do not have special image to illustrate the work I have donne these last 8 hours, sorry 🙇♂️"}, {"post_id": 57267, "ts": "2026-08-29T11:33:25Z", "hours": 2.5878, "text": "DevLog #27 : I finalized the sync of all the part of the system that manage indicators calculation with the new core of MarketLab. The next step is to do the same with the events part. Thanks for reading 😆 PS: Special thanks to @bali0531 for liking DevLog #26 !"}, {"post_id": 56754, "ts": "2026-08-28T17:15:42Z", "hours": 10.6694, "text": "DevLog #26 : I keep on modifying the core of MarketLab. I do not have anything special but I need to do this DevLog to log my hours. Thanks fore reading 😄"}, {"post_id": 54023, "ts": "2026-08-24T19:26:44Z", "hours": 8.7467, "text": "DevLog #25 : New feature ! While trying to calculate an indicator, if an existing one has the same name, MaketLab will ask if you want to replace the existing one, rename the indicator you want to calculate or simply cancel the calculations. I am working on the same system but with MarketLab looking on the parameters of calculated indicators. Thanks for reading ✌️!"}, {"post_id": 53139, "ts": "2026-08-23T16:00:46Z", "hours": 8.6025, "text": "DevLog #24 : I standardized indicator definition and metadata to set the rules the user must follow to create indicator that can be accepted by MarkLab. The user must define these metadata to use its own custom indicators : the name of the indicator, the number of parameters nb that the user can choose, the recommended parameters r , the function of the indicator f , the type t of the indicator ( general_app or custom ) and the forced parameters fp . The next step is to do the same with the events. Thanks for reading 🙌!"}, {"post_id": 51812, "ts": "2026-08-21T17:32:26Z", "hours": 8.0319, "text": "DevLog #23 : I keep on working on redefining the structure of the code. All the sqlite3 operations are now performed by the class MarketLabDataBase . Redefining the structure af the code will take a long time (8 hours for one step) but it is one of the final steps of the project and it will hopefully improve the complexity grade of my project. Thanks for reading 😆!"}, {"post_id": 51280, "ts": "2026-08-20T20:27:26Z", "hours": 3.5481, "text": "DevLog #22 : I started working on redefining the structure of the code to strengthen robustness and clarify the architecture. I asked Codex to make me a check list of all the modifications to operate. Thanks for reading 😁 PS : Special thanks to @Aaravkatiyar for liking and commenting DevLog #21 and following ! (You are the GOAT!)"}, {"post_id": 51044, "ts": "2026-08-20T15:07:02Z", "hours": 8.0183, "text": "DevLog #21 : New functionality ! You can now download data from a specific date range. I added this feature to prevent long downloads ( ~ 5 minutes for the whole history with a time frame of 15m and 15 times longer for a timeframe of 1m ). I am now working on computing series with incompatible indicators with general_application . Thanks for reading ✌️! PS : Special thanks to @alyaanmughal for liking DevLog #18, @Aaravkatiyar for liking DevLog #20 and @Sreeni for following me !"}, {"post_id": 49474, "ts": "2026-08-18T10:20:12Z", "hours": 3.0044, "text": "DevLog #20 : New design ! I redesign the main menu by moving 🧩 Manage Custom Indicators and Events , 🗑️ Delete a Column and 📁 Open or Create a Database in the ⚙️ Settings . Small change but it improves the user experience and the understanding of the main menu. Thanks for reading 😄!"}, {"post_id": 48927, "ts": "2026-08-17T14:20:06Z", "hours": 2.0031, "text": "DevLog #19 : New functionality ! You can now compute series of the same indicator but with different windows (EMA 20; EMA 30; EMA 40 …). It only work for the functions compatible with general_application for now but I am working to extend this feature to the custom indicator functions. Thanks for reading 🙌!"}, {"post_id": 48352, "ts": "2026-08-16T17:43:53Z", "hours": 4.1689, "text": "DevLog #18 : New indicator and new event ! The filter_savgol applies a Savitzky-Golay filter (filter that works on polynomial regression) and peak_detection . This led me to add a system that allows the use of indicators and events functions that do not comply with the functions general_application and general_event_application (functions that simplify the création of custom indicators and events). The next steps are the packaging and the writing of the README. Thanks for reading 🙌!"}, {"post_id": 47705, "ts": "2026-08-15T19:53:16Z", "hours": 7.8397, "text": "DevLog #17 : New functionality ! You can now create a file containing your own custom indicators and events . I also created a function called general_event_application which simplifies the creation of new events. Thanks for reading 😄 PS : Special thanks to @bali0531 for liking DevLog #16 !"}, {"post_id": 44678, "ts": "2026-08-10T16:24:41Z", "hours": 3.4011, "text": "DevLog #16 : New functionality ! You can now calculate statistics of an event to show up in function of two parameters. In The example, the event is a cross between the close and the SMA 100 in function of the volume and the the volume of the quote. I am coming to the end of the project, the next steps are to allowing the user create its own indicators and events. Thanks for reading 🙌 ! PS : Special thanks to @MathBty and @dhruvalya for liking DevLog #15 !"}, {"post_id": 43280, "ts": "2026-08-08T13:27:43Z", "hours": 2.3211, "text": "DevLog #15 : New functionality ! You can now search a file already created to make returning to work easier on an existing file. Thank for reading 😄 ! PS : Special thanks to @Gerafftes for liking DevLog #14 !"}, {"post_id": 42185, "ts": "2026-08-06T20:01:07Z", "hours": 1.8761, "text": "DevLog #14 : New fonctionality ! You can now plot a Status with indicators directly with the interface. In the example, Close, SMA 100, SMA 50 and Cross(SMA 100, SMA 50) are plotted on the same graph. The next step is to calculate Stats and plot it too. Thanks for reading 🫡"}, {"post_id": 41992, "ts": "2026-08-06T15:11:05Z", "hours": 5.2839, "text": "DevLog #13 : New functionalities ! You can now plot any indicators already calculated directly with the interface. The next step is to plot status (like “cross”) and stats. I also added 4 functional indicators in the indicator dict of the interface (ATR, VWMA, Amplitude and Relative). The Indicator Dict is able to contain recommended parameters for each indicator and they are printed when entering parameters to calculate indicators. Thanks for reading 🫡. PS : Special thanks to @Jinan (again!), @CoolDuck and @MathBty for liking DevLog #12, @PAPER_01 for liking my first DevLog. Thanks again to @jinan for the amazing comment about DevLog #12 and @yeshwingadde for following !"}, {"post_id": 41322, "ts": "2026-08-05T13:45:46Z", "hours": 2.9131, "text": "DevLog #12 : Bugs fixed ! I added in the code verifications for each user entry to prevent incorrect type of entries and reduce program crash. I also added the option Continue after an action. Hope you like the new logo. Thanks for reading 🙌! PS: Special thanks to @shi , @Jinan and @f for liking DevLog #11. Thank again to @f for the nice comment about DevLog #11!"}, {"post_id": 41198, "ts": "2026-08-05T08:37:29Z", "hours": 4.0861, "text": "DevLog #11 : New functionality ! You can now calculate indicators directly with the interface and delete it. I also added an History to remember the actions that have been done. Hope you like the new design with emojis. I am still taking ideas from you. Thanks for reading 😁"}, {"post_id": 40775, "ts": "2026-08-04T14:59:09Z", "hours": 1.1947, "text": "DevLog #10 : New functionality ! I have started to build a terminal-based user interface to make Market Lab much easier to use. Here is a preview of the first functionality : Download Data to download every candles of the selected symbol and interval. I am also continuing to work on the heat map feature (look at DevLog #9). If you have advice about the design or whatever of the interface, I will be very happy to listen to it. Thanks for reading 🫶 !"}, {"post_id": 40616, "ts": "2026-08-04T08:41:34Z", "hours": 1.0947, "text": "DevLog #9 : New functionality ! The probability for an event occurring can now be calculated and plotted. In the example, the probability of a crossover/crossunder based on trading volume is shown in red or in blue. The next step is to calculate the probability of an event to show up based on two variables and visualize the results using heatmaps. Thanks for reading 🙌. PS : Special thanks to @jinan , @logannavaia and @Epicninja3 for liking the DevLog #8. Thanks again to @Epicninja3 and @logannavaia for following this project !"}, {"post_id": 40386, "ts": "2026-08-03T21:08:16Z", "hours": 1.6072, "text": "DevLog #8 : New event ! The event “under_over” allows us to detect a moment in time where a variable is under or over another. Here the example shows the moment when the SMA 50 is either under or over the SMA 100. I keep on working on the function that allows to compute the probability of an event to show up based on different variables. Thanks for reading 🤠 PS : special thanks to @f for liking the DevLog #7 and the very encouraging comment !"}, {"post_id": 40269, "ts": "2026-08-03T18:04:16Z", "hours": 0.4303, "text": "DevLog #7 : Following the DevLog #6. Triangular labels can now be plotted on the graph at the location of each event. In the example, the event is the crossover between two EMAs. The next step is to compute the probability for an event to show up based on different variables such as the volume, the number of trades etc. Thanks for reading ✌️ ! PS : special thanks to @benvelzul for following this project !"}, {"post_id": 40182, "ts": "2026-08-03T16:13:51Z", "hours": 1.2819, "text": "DevLog #6 : New event ! The event « cross » allows us to detect a moment in time when two values cross each other. Here, the example shows the moment when two SMAs cross one another. This event is shown by the second graph which ocilliates between 1 and -1 for a crossover and a crossunder. The next step is to plot labels directly on the graph for each « cross ». Thanks for reading 😄"}, {"post_id": 39966, "ts": "2026-08-03T09:35:00Z", "hours": 0.3058, "text": "DevLog #5 : New indicator ! I have juste finished implementing the Commodity Chanel Index (cci). This indicator show how much the deviation of the price form its average (SMA) is important relatively to the mean deviation of the price (MD). The math formula is a bit complexe so I joined it below. The typical price (TP) is the average of the high (H), low (L) and close (C) of the same candle. Thanks for reading this DevLog 🫡"}, {"post_id": 39947, "ts": "2026-08-03T08:48:30Z", "hours": 0.485, "text": "DevLog #4 : New indicator ! I have just finished implementing the Stochastic Oscillator (or %K). It is a simple indicator which calculate the “position” of the close of the the last candle over n candles. Here is the math formula: %K = 100 * (Close-Low)/(High-Low) with Close the close of the last candle ; Low the lowest point over n candles ; High the highest point over n candles. The timeframe is still 12h because the calculations are much more faster than those for a 1h timeframe ( logically 12 time faster). Thanks for reading this DevLog 🙌"}, {"post_id": 39921, "ts": "2026-08-03T07:32:41Z", "hours": 1.06, "text": "DevLog #3 : I have just finished implementing the RSI calculation. This is the RSI 14 close of the BTC on a 12h timeframe. I have more than 10 indicators now !"}, {"post_id": 39612, "ts": "2026-08-02T18:27:40Z", "hours": 0.6106, "text": "DevLog #2 : New Indicator ! Here is the ATR 14 of BTC since the 17/08/2017 computed on a 12h time frame. Thanks for reading 😆!"}, {"post_id": 39559, "ts": "2026-08-02T16:27:33Z", "hours": 12.2767, "text": "DevLog #1 : Very first DevLog ! I started Market Lab one week ago and heard about StarDance Challenge one month ago. But I got the idea to participate only yesterday so I have about ten hour that won’t count 😥. I already established the data extraction from BINANCE API. I keep on tracking errors and creating new functions to calculate indicators. Thanks for reading 🫡!"}]...
|
Title: Market Lab by @GoldenClarinet | Stardance
Title: Market Lab by @GoldenClarinet | Stardance
Devlogs:
DevLog #1 : Very first DevLog ! I started Market Lab one week ago and heard about StarDance Challenge one month ago. But I got the idea to participate only yesterday so I have about ten hour that won’t count 😥. I already established the data extraction from BINANCE API. I keep on tracking errors and creating new functions to calculate indicators. Thanks for reading 🫡!
DevLog #2 : New Indicator ! Here is the ATR 14 of BTC since the 17/08/2017 computed on a 12h time frame. Thanks for reading 😆!
DevLog #3 : I have just finished implementing the RSI calculation. This is the RSI 14 close of the BTC on a 12h timeframe. I have more than 10 indicators now !
DevLog #4 : New indicator ! I have just finished implementing the Stochastic Oscillator (or %K). It is a simple indicator which calculate the “position” of the close of the the last candle over n candles. Here is the math formula: %K = 100 * (Close-Low)/(High-Low) with Close the close of the last candle ; Low the lowest point over n candles ; High the highest point over n candles. The timeframe is still 12h because the calculations are much more faster than those for a 1h timeframe ( logically 12 time faster). Thanks for reading this DevLog 🙌
DevLog #5 : New indicator ! I have juste finished implementing the Commodity Chanel Index (cci). This indicator show how much the deviation of the price form its average (SMA) is important relatively to the mean deviation of the price (MD). The math formula is a bit complexe so I joined it below. The typical price (TP) is the average of the high (H), low (L) and close (C) of the same candle. Thanks for reading this DevLog 🫡
DevLog #6 : New event ! The event « cross » allows us to detect a moment in time when two values cross each other. Here, the example shows the moment when two SMAs cross one another. This event is shown by the second graph which ocilliates between 1 and -1 for a crossover and a crossunder. The next step is to plot labels directly on the graph for each « cross ». Thanks for reading 😄
DevLog #7 : Following the DevLog #6. Triangular labels can now be plotted on the graph at the location of each event. In the example, the event is the crossover between two EMAs. The next step is to compute the probability for an event to show up based on different variables such as the volume, the number of trades etc. Thanks for reading ✌️ ! PS : special thanks to @benvelzul for following this project !
DevLog #8 : New event ! The event “under_over” allows us to detect a moment in time where a variable is under or over another. Here the example shows the moment when the SMA 50 is either under or over the SMA 100. I keep on working on the function that allows to compute the probability of an event to show up based on different variables. Thanks for reading 🤠 PS : special thanks to @f for liking the DevLog #7 and the very encouraging comment !
DevLog #9 : New functionality ! The probability for an event occurring can now be calculated and plotted. In the example, the probability of a crossover/crossunder based on trading volume is shown in red or in blue. The next step is to calculate the probability of an event to show up based on two variables and visualize the results using heatmaps. Thanks for reading 🙌. PS : Special thanks to @jinan , @logannavaia and @Epicninja3 for liking the DevLog #8. Thanks again to @Epicninja3 and @logannavaia for following this project !
DevLog #10 : New functionality ! I have started to build a terminal-based user interface to make Market Lab much easier to use. Here is a preview of the first functionality : Download Data to download every candles of the selected symbol and interval. I am also continuing to work on the heat map feature (look at DevLog #9). If you have advice about the design or whatever of the interface, I will be very happy to listen to it. Thanks for reading 🫶 !
DevLog #11 : New functionality ! You can now calculate indicators directly with the interface and delete it. I also added an History to remember the actions that have been done. Hope you like the new design with emojis. I am still taking ideas from you. Thanks for reading 😁
DevLog #12 : Bugs fixed ! I added in the code verifications for each user entry to prevent incorrect type of entries and reduce program crash. I also added the option Continue after an action. Hope you like the new logo. Thanks for reading 🙌! PS: Special thanks to @shi , @Jinan and @f for liking DevLog #11. Thank again to @f for the nice comment about DevLog #11!
DevLog #13 : New functionalities ! You can now plot any indicators already calculated directly with the interface. The next step is to plot status (like “cross”) and stats. I also added 4 functional indicators in the indicator dict of the interface (ATR, VWMA, Amplitude and Relative). The Indicator Dict is able to contain recommended parameters for each indicator and they are printed when entering parameters to calculate indicators. Thanks for reading 🫡. PS : Special thanks to @Jinan (again!), @CoolDuck and @MathBty for liking DevLog #12, @PAPER_01 for liking my first DevLog. Thanks again to @jinan for the amazing comment about DevLog #12 and @yeshwingadde for following !
DevLog #14 : New fonctionality ! You can now plot a Status with indicators directly with the interface. In the example, Close, SMA 100, SMA 50 and Cross(SMA 100, SMA 50) are plotted on the same graph. The next step is to calculate Stats and plot it too. Thanks for reading 🫡
DevLog #15 : New functionality ! You can now search a file already created to make returning to work easier on an existing file. Thank for reading 😄 ! PS : Special thanks to @Gerafftes for liking DevLog #14 !
DevLog #16 : New functionality ! You can now calculate statistics of an event to show up in function of two parameters. In The example, the event is a cross between the close and the SMA 100 in function of the volume and the the volume of the quote. I am coming to the end of the project, the next steps are to allowing the user create its own indicators and events. Thanks for reading 🙌 ! PS : Special thanks to @MathBty and @dhruvalya for liking DevLog #15 !
DevLog #17 : New functionality ! You can now create a file containing your own custom indicators and events . I also created a function called general_event_application which simplifies the creation of new events. Thanks for reading 😄 PS : Special thanks to @bali0531 for liking DevLog #16 !
DevLog #18 : New indicator and new event ! The filter_savgol applies a Savitzky-Golay filter (filter that works on polynomial regression) and peak_detection . This led me to add a system that allows the use of indicators and events functions that do not comply with the functions general_application and general_event_application (functions that simplify the création of custom indicators and events). The next steps are the packaging and the writing of the README. Thanks for reading 🙌!
DevLog #19 : New functionality ! You can now compute series of the same indicator but with different windows (EMA 20; EMA 30; EMA 40 …). It only work for the functions compatible with general_application for now but I am working to extend this feature to the custom indicator functions. Thanks for reading 🙌!
DevLog #20 : New design ! I redesign the main menu by moving 🧩 Manage Custom Indicators and Events , 🗑️ Delete a Column and 📁 Open or Create a Database in the ⚙️ Settings . Small change but it improves the user experience and the understanding of the main menu. Thanks for reading 😄!
DevLog #21 : New functionality ! You can now download data from a specific date range. I added this feature to prevent long downloads ( ~ 5 minutes for the whole history with a time frame of 15m and 15 times longer for a timeframe of 1m ). I am now working on computing series with incompatible indicators with general_application . Thanks for reading ✌️! PS : Special thanks to @alyaanmughal for liking DevLog #18, @Aaravkatiyar for liking DevLog #20 and @Sreeni for following me !
DevLog #22 : I started working on redefining the structure of the code to strengthen robustness and clarify the architecture. I asked Codex to make me a check list of all the modifications to operate. Thanks for reading 😁 PS : Special thanks to @Aaravkatiyar for liking and commenting DevLog #21 and following ! (You are the GOAT!)
DevLog #23 : I keep on working on redefining the structure of the code. All the sqlite3 operations are now performed by the class MarketLabDataBase . Redefining the structure af the code will take a long time (8 hours for one step) but it is one of the final steps of the project and it will hopefully improve the complexity grade of my project. Thanks for reading 😆!
DevLog #24 : I standardized indicator definition and metadata to set the rules the user must follow to create indicator that can be accepted by MarkLab. The user must define these metadata to use its own custom indicators : the name of the indicator, the number of parameters nb that the user can choose, the recommended parameters r , the function of the indicator f , the type t of the indicator ( general_app or custom ) and the forced parameters fp . The next step is to do the same with the events. Thanks for reading 🙌!
DevLog #25 : New feature ! While trying to calculate an indicator, if an existing one has the same name, MaketLab will ask if you want to replace the existing one, rename the indicator you want to calculate or simply cancel the calculations. I am working on the same system but with MarketLab looking on the parameters of calculated indicators. Thanks for reading ✌️!
DevLog #26 : I keep on modifying the core of MarketLab. I do not have anything special but I need to do this DevLog to log my hours. Thanks fore reading 😄
DevLog #27 : I finalized the sync of all the part of the system that manage indicators calculation with the new core of MarketLab. The next step is to do the same with the events part. Thanks for reading 😆 PS: Special thanks to @bali0531 for liking DevLog #26 !
DevLog #28 : I finalized the sync of all the part of the system that manage events calculation with the new core of MarketLab. I also started the README. I am currently working on updating all the data after a break in your work on MarketLab. Thanks for reading ✌️! PS: Special thanks again to @bali0531 for liking DevLog #27 ! PS 2: I do not have special image to illustrate the work I have donne these last 8 hours, sorry 🙇♂️
DevLog #29 : New feature ! You can now update the data available on Binance API. I am also working on calculating indicators and avents over these new data. Thanks for reading 😄 PS: If you know how the 3D Printing Credits available in the shop can be used, I will be grateful if you tell me in the comments!
DevLog #30 : New feature (not perfect yet) ! You can now update the calculated indicators, but an issue just showed up: there are three misplacement of the new values of the indicator. I am working on fixing that. Thanks for reading 🫡 PS : Special thanks to @bali0531 , @lakshaykhatri780 and @jinjin for liking DevLog #29 !
DevLog #31 : New feature (not perfect yet) ! I am fixing the issues on the indicator update (see DevLog #30). The indicator is now updated over te whole set of new data and a misplacement has been corrected. But some problems remains: the indicator is updated with what seems to be the first value of the whole set of data and it is also calculated with the very first values (that normally cannot be done: if you are calculating a SMA100, the 99 firsts values cannot be calculated) Thanks for reading 😄
DevLog #32 : Newfeature ! : I finally fixed the bug that misplaced and made calcultation over wrong data to update indicators. Thes next step is to add the update for the events. Thanks for reading 😄 PS : Special thanks to @kingkev for liking DevLog #31 and following and thank to @No1Michael for liking DevLog #30 !
Final DevLog (#33) : Great Great Great New !! The first version of MarketLab is finished !! You can now install it, if you have python with a simple pip install the-marketlab-project and import it under the name of marketlab ! Thanks for reading ad for all the support you gave me 😄 ✌️ ! Special thanks to @dapsvi , @jinjin , @preyas_21 and @MathBty for liking DevLog #33 !
After DevLog #1 : I have edited parts of the README and fixed a bug when window selection. Thanks for reading 😄
After Ship DevLog #2: I changed manually the README to reduce the part of AI code
Ships:
MarketLab is a python tool to exploring BINANCE financial data. With MarketLab, you can create and manage database of market data such as the OHLCV, calculated indicators such as SMA, RSI etc… The most challenging part was to rebuilt the core of the project. I am very proud of MarketLab because it is my very first and my most complex python project yet....
|
1792857027.3014672
|
1792943427.3014672
|
|
9616
|
revo by @ratmuds | Stardance
|
ratmuds
|
1
|
134.0
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-21T01:26:40Z", "hours": 134.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "I wanted to make a robot arm that punched above it’s weight. The price is pretty similar to smaller robot arms on Amazon that may not include cameras, which would need to be purchased seperately. The way this is achieved by using cheap servos, servos that are 100% NOT made for this purpose, and adding most of the functionality of a smart servo back by using a TON of PCBs. It was a lot of soldering. I just wanted to make something that had more features for less money :) This project was SUPER difficult for me, and pushed my limits a bit. The amount of tolerences especially for the stupid custom motor case and magnet and PCBs and the crazy mainboard and generally having everything play well together took so long, I had many days where I quit frustrated. I’m very happy about how it turned out though, even seeing the lots of shortcomings it has.", "repo": "https://github.com/ratmuds/revo", "demo": ""}]...
|
[{"post_id": 71545, "ts": "202 [{"post_id": 71545, "ts": "2026-09-21T01:17:14Z", "hours": 1.7336, "text": "Finish Project Up :) I wrote a ton in the repository im tired :( https://github.com/ratmuds/revo"}, {"post_id": 71258, "ts": "2026-09-20T15:14:42Z", "hours": 3.1572, "text": "Setup VR and Gripper, Robot Rapidly Disassembles my robot arm is broken I setup VR and fixed some more IK when I go into VR and try playing around, it instantly flies out and throws itself off the table and breaks into pieces. This is going to be fun to fix."}, {"post_id": 70809, "ts": "2026-09-19T19:55:52Z", "hours": 3.8861, "text": "Integration Hell I’ve just been like trying to get this stupid project to work. Screws keep falling out, the IK keeps failing, things are hitting each other and wires are being strained I’m in integration hell. Everything works fine on its own, but combining everything is horrible. I’ve been reassembling and trying to fix this stupid thing for so long. Anyway I’ve went through a few more revisions of some CAD and some more refinements and the gripper and camera is now mounted but this is just incredibly incredibly annoying :(((("}, {"post_id": 69881, "ts": "2026-09-18T00:05:17Z", "hours": 3.8311, "text": "Fixed Broken Magnet, Redoing all IK joint data and offsets, Stereo depth test An encoder magnet broke off, so I had to disassemble and reassemble the top half of the arm to get to the encoder magnet and glue it back on. I also started properly punching in the IK joint data, before it was mostly just random data but now almost all of the data is mostly correct, and I’m waiting for a print to complete that will flip a motor to finally get the IK simulation matching with real life and have the IK endpoint be tracked at the tip of the gripper. It was a lot of trial and error and measurements. I also started experimenting with adding depth estimation. Eventually this could add collision avoidance IK pathfinding, which would be really cool but it’s just a pointcloud in the render right now. But the robot arm is slowly coming together finally! I’ll be able to grab something soon I think."}, {"post_id": 68903, "ts": "2026-09-16T00:31:34Z", "hours": 2.8461, "text": "Assembled Full Arm (not all motors active yet for safety) and Basic Interpolation for Control I assembled the new full arm assembly minus the end effector and didn’t plug in the first two motors as they have the highest chance of doing something really bad. I also made some general changes to the code and also added a flag to some motors to interpolate their movement slowly to not explode the robot arm while controlling it."}, {"post_id": 68352, "ts": "2026-09-15T00:17:44Z", "hours": 2.5044, "text": "Significantly Better Motor Control and Gripper CAD The motor code was getting a bit messy, so I rewrote it. I separated the motor code from the main control code which cleaned it up a bit. LADRC I’m using LADRC now. It’s kind of like PID, but advanced. I took the implementation from online. I feed it the motor current, encoder value, and the target and it goes. The algorithm or whatever somehow calculates the velocity of the motor and how much “effort” it’s using and then gives an effort to put to the motor. It somehow cancels out like disturbances like gravity, backlash, the general physics, etc. As you can see, the motor control is so so so much better than before. It cleanly decelerates. It handles load and dynamically increases effort. Before there was some oscillation because it just couldn’t figure out how to get to the target, this one is just so much better and so hands off. There’s basically like no calibration as it does it over time :)))"}, {"post_id": 67779, "ts": "2026-09-14T00:57:25Z", "hours": 1.4489, "text": "Assembled last 4 DoF for Integrated Test I assembled the MG996R servos in a test piece that will represent the full regular arm segment to test if everything worked. With some fiddling it worked. As you can see there’s more transparent pieces. The pieces that connect the previous joint are transparent to show the Neopixel below it a lot better as before the LED was almost completely occluded. The bright green LEDs power status ones are too bright but I can cover them later."}, {"post_id": 67735, "ts": "2026-09-13T23:20:04Z", "hours": 6.6769, "text": "Dashboard Revamp WIP, Tons of CAD changes, Code Updates, Tolerances I’ve just been refining everything and it’s just going really slowly. Many problems. Magnets shorting out stuff. Magnet too far and not being detected. Wires breaking. I’ve also redesigned the dashboard UI to hopefully look better. I’m still working on it, hopefully it will look more polished later. The dashboard It shows a load speedometer type thing, with the load (max current from a motor out of 500mA) and the temp on the right (for now just dummy value) The motors have cool little animations now too and look less cluttered. I’ve just been working on it a lot. Just everything in general tbh."}, {"post_id": 65061, "ts": "2026-09-09T22:13:56Z", "hours": 5.7644, "text": "Debugging and Fixing Is Killing Me and the Mainboard Exploded :( I have spent SO many hours trying to get this to work. It’s so annoying. This isn’t near how many hours I spent, I’ve been too frustrated to log it. The case is incredibly annoying. Getting the initial case works fine. Doing all of this stuff makes it complicated and break and jam up. The RS485 busses just randomly stop working. The servo output is wiggly and not stable. A PCB header snapped and I didn’t have replacements so I had to bodge solder it back on. The mainboard voltage regulator literally exploded. It was a cheap part but yeah that literally just broke stuff. Fixes to everything. CAD. Firmware. bruh"}, {"post_id": 60577, "ts": "2026-09-03T00:49:54Z", "hours": 6.7731, "text": "MG996Rs Finally Working The servos finally work. I don’t even think this amount of hours logged is equal to how long this took, because I just got tired of it. I think I printed like 20 new revisions of the case and servo mount. It was just so so annoying. While the general case worked, getting a reliable magnet output shaft was impossible. From tolerances, to my own errors to binding to hot glue getting everywhere, or superglue binding random gears, or my getting my fingers stuck to the actual case from superglue (was not fun and hurt) The case finally works. And the prototype code somewhat works too. It basically rotates until the sensor hits zero, it resets the “ticks”. A tick is a raw output from the AS5600. Because this magnet shaft spins faster than the output shaft, like 10x faster, the MCU needs to poll the sensor super quickly, which also needed some optimizations to clear the blocking bus communication wait that would let the magnet skip multiple full turns. The sensor doesn’t internally count turns so we just need to count manually and detect when it jumps from 4096-0 or the other way around. It then spins around until the sensor is hit AGAIN to calculate tick to degrees, assuming the sensor was zeroed before, and then the current ticks would equal 360 degrees. Another problem was the servo needs a PWM signal to move. The threshold from stop to go is pretty garbage on these cheap servos, leading it to spin quite fast already when at the lowest PWM value. Instead when far from the value it increases the PWM signal, closer it brings to minimum and starts linearly flickering the PWM signal from 0 to the speed, with the closer it is the more ratio of time stopped to inch it closer to the target. It actually surprisingly works. It’s very fragile though, any skipped reads, from potentially slow bus transfers would completely throw it off, so all the code running locally on the weak 8 bit MCU needs to be extremely lightweight. Yeah this was super super painful. I’ve been sitting here redoing superglue and code for hours."}, {"post_id": 58979, "ts": "2026-08-31T15:36:35Z", "hours": 3.4036, "text": "Servo Code I did some slight refining to the servo case again, making the tolerances tighter to keep the gears from wobbling. It’s still sort of loose, so I need to just keep trying over and over again. I’m also still writing the servo code. It’s already much more organized than before. I’m also learning Vim keybinds and stuff to try and code faster. The RS485 bus communication works now, and it’s just sending dummy telemetry data back right now. It can also control the servo fine. I’ve just been doing like the lower level architecting I guess, or not really I’m not sure. Before everything was crammed into a single file and now there’s actually header files and multiple source files and it’s actually somewhat readable. I had an issue with LED control for a second but that’s fixed now :)"}, {"post_id": 58467, "ts": "2026-08-30T21:44:37Z", "hours": 2.4922, "text": "V2 Servo Case Working; Servo Code Revamp WIP After around 10 more revisions, the new servo case is fully working with the magnetic extender. The V2 is significantly better designed and higher efficiency than the last, more guess and check result. I’ve also started to rewrite the servo code. Right now, its really messy and just kind of snowballed over time as I’ve slapped on patches and features to a fundamentally weak base. I’m in the progress of writing that now, hopefully it will be done soon. It will also include the new auto calibration and encoder usage and PID (and potentially advanced auto tuned curve to combat motor acceleration overshoot and backlash)"}, {"post_id": 57778, "ts": "2026-08-30T02:16:49Z", "hours": 3.8514, "text": "Working on IK System & Resoldered Joints Again Bruh so my robot arm after finally being assembled by the way exceeded it’s limits and tore its wires out. I had to take apart the mainboard assembly again, which just is SO TEDIOUS. I just dumped solder and hot glue again and fixed a short caused from another wire being sheared off. Also I’m working on the IK system, but it’s not really going well right now. I need to add offsets and config to each of the motors to get them to work correctly. The motors hooked up right now started crashing into my table sadly. Hopefully it will work soon. I also continued working on the servo case. I still haven’t added the code to calibrate and get closed loop control on the MG996R servos. The DS5180 encoders should be easier though."}, {"post_id": 57272, "ts": "2026-08-29T11:42:11Z", "hours": 3.1006, "text": "Reprinted Arm Segments; Added Segment Cover; Servo Mount Working; Working on Web UI I reprinted the arm segments, hopefully for one of the last times, I’ve went through so much filament already. It now allows the mainboard to be neatly packed inside, hiding most of it while still allowing wires to come out. Due to the design of the mainboard, the wires do have to stick out the front like that sadly. To avoid crushing wires, the servo mount has been expanded to allow more clearance for wires and general electronics. The servo mount works now with a new design. I remeasured and adjusted the sketches and added proper hole sizes and a little chamfer to allow the insert to slot in before using a soldering iron to melt it in to help align much better than before. The web UI now shows a representation of the arm. It’s not tuned yet, so the angles are wrong but I’ll fix it soon. I had to move everything from Fusion to Onshape to create the assemblies, and then use a Python library to convert the Onshape assembly into a URDF file that can be rendered with the correct joints inside ThreeJS. Next, I’ll be adding IK and general arm control."}, {"post_id": 56800, "ts": "2026-08-28T18:32:28Z", "hours": 3.1978, "text": "Heatset Inserts Working; Fixing Faulty Motor Power Connections; Internal Fan Power Connectors I verified that the servo cover works, and it’s much much better than the last version. The tolerances are much better this time, leading to less stress on the gears. The same amount of turning uses around 100mA compared to 300mA before, almost reaching it’s limit without any load. Some motor power connectors disconnected, so I found it with a multimeter and added hot glue to some connectors to secure them. I soldered the fan power things inside the PCB, but realized that the JST connectors I’m using are different from the fans. I might have to make a sort of adapter or something by crimping more wires. And the heatset inserts finally work after some tweaking, before the inserts would get misaligned when put in, now with a aligning lip thing it can be slotted in and melted in more accurately."}, {"post_id": 56316, "ts": "2026-08-28T01:34:32Z", "hours": 4.6306, "text": "New Mainboard Cover, Wiring, Bearing Revamp, Heatset Inserts, Servo Cover I added space for the mainboard segment, now it has clearance to actually fit, I redid the cover for this segment, and now it can allow the wires to come out The servo mount now has heatset inserts, because the previous screw in print kept slipping out, but this time the screws just would not go into the inserts through the mount for some reason? The screws went in normally, but with the tolerances, when multiple screws go in they just fight and push against the edge of the insert instead of going in. The MG996R servo housing now has been redesigned, based off of picture references and calipers to attempt to be more accurate than the last one to try and reduce the amount of bad tolerances affecting the gears and making them misaligned, reducing torque output. And just general wiring and CAD work."}, {"post_id": 55294, "ts": "2026-08-27T01:14:32Z", "hours": 2.9064, "text": "Assembled Basic Arm Parts and Packaged Main Board Assembly; Reprogrammed servo MCUs & worked on web UI I redid some soldering on the mainboard PCB, secured connections, and secured everything with tape for now. I’ll likely have to reprint the first arm segment to fit everything in flush because as you can see there are wires and things bulging out of the segment itself right now. I also redesigned the CAD to include heat set inserts. I did programming and a bunch of bug testing and fixing too. An hour of time was lost because Lapse errored out :((((( very sad about that :( I also had an issue with data corruption on the RS485 bus but that is fixed now, and it works like it should."}, {"post_id": 54840, "ts": "2026-08-26T00:29:08Z", "hours": 4.3236, "text": "Mainboard Stack Redo & Firmware Updates and Test Web UI I had to take the electronics out, and redo some of the wiring. The screw terminals are pretty cheap and suck, which makes it incredibly annoying to use as it’s impossible to fit these thicker AWG wires in and they sometimes slide out of the terminals. When I’m done I’m probably going to hot glue the wires. The incredibly fragile flexible extension jumpers for data transfer between the stack is also super fragile, and especially doesn’t grip. Sometimes the data wires just slip out. I took apart the arm, as during testing the motor stripped some screws. I’ve ordered some heat set inserts to prevent that from happening again, which will require me to reprint all the arm segments again. I also added a simple web UI for moving the joints. There is an empty ThreeJS sidebar and the right has some graphs (real data) from the mainboard sensors. The smaller MG996R servos haven’t been mounted yet due to a power delivery issue from the mainboard but hopefully that will be fixed soon."}, {"post_id": 28657, "ts": "2026-07-15T01:25:27Z", "hours": 4.0167, "text": "Test assembly and fitting of robot arm I am mostly done with the core functionality of the mainboard, so i packed everything together and double checked eerything works including power Then I started assembling most of the servos The smaller servo casing doesn’t fit very well, and the transparent window doesn’t fit either But the magnetic cover for the arm segments works great :) It was a lot of fitting, screwing, testing with multimeter, etc"}, {"post_id": 28409, "ts": "2026-07-14T17:26:49Z", "hours": 5.1303, "text": "Finalizing hardware I crimped and soldered almost the remaining wires, I’ve made some last redesigns to the arm CAD and I’m now fitting everything into the new prints and assembling. I’ve also had a ton of trouble with my servo PCBs lately, but I found out a lot of the issue was my programmer is actually fried somehow, so I’m now using an old RPi Pico to act as a UPDI adapter so PlatformIO can now successfully program the ATtinys much better now. A lot of the CAD was slightly tweaked so that everything fits better"}, {"post_id": 26853, "ts": "2026-07-11T21:36:43Z", "hours": 1.175, "text": "Servo firmware almost done The servo firmware now (with the mainboard) sends validation data back and forth so both the servo and mainboard can verify that the data was sent successfully and can ignore corrupted packets because my wiring can be flaky sometimes The firmware now sends real data again and is much more robust, with a proper stateful LED that shows useful information, like offline, online and driving motor, homing, error (fast flashing of LED as red), and the error state is also triggered in the case of corrupted packets (I tried it and it works :)) I’m also still printing out the pieces to assemble the arm. I actually have more hours working on it, I just forgot to log :((((((("}, {"post_id": 26218, "ts": "2026-07-10T21:40:32Z", "hours": 8.1217, "text": "Rewrote all firmware; Fixing Servo Cover and a ton of other CAD; Almost fried everything For the CAD, it doesn’t look like much changed, but I’ve basically redesigned a bunch of things and fixed tolerances and now it should move really smoothly. With the bearings added, everything moves incredibly smoothly now Especially the servo cover was annoying. I needed to attach a magnet to the shaft which was really annoying. I haven’t logged that well, I’ve done more like 15 hours of work instead of just 5 but whatever Most of those hours was the painful debugging. RS-485 busses, JSON not communcating with the PC, dropped connections, corrupted data, disconnected jumpers, FTDI programmer becoming flaky for some reason It’s just been pain :((( Now the PC (screenshot) recieves all the data and sends all the data You can see a lot (not all) of the telemetry being received. Ignore the volts though my math is wrong. Realtime connecting now works smoothly, as you can see the other RS485 servos are offline."}, {"post_id": 24091, "ts": "2026-07-07T13:11:08Z", "hours": 5.9586, "text": "Bidirectional Communication To PC/RPi 5 with LeRobot & More CAD changes I setup a basic class for my robot in LeRobot (which was a little confusing) and then I also got communication between the Pico and my PC working. Originally I have SPI pins exposed on my mainboard PCB to connect to the RPi 5’s GPIO pins, but for some reason after lots of debugging, even running it at super low speeds it just would not work. So I switched to just using serial over USB, and using the SerialTransfer library to send data more reliably. The script on the PC now can send target joint angles, and receive a lot of data back, like if the joint is online, temp, current, angle, homing sensor, home status, LED, firmware version, voltage, etc I also iterated on the CAD for everything, and it should be done soon. It’s just a bunch of tolerance fixes to make everything fit at this point and some new things like mounts for encoders and DC buck modules and fixing the CAD for the custom MG996R case."}, {"post_id": 23468, "ts": "2026-07-06T13:46:37Z", "hours": 1.9767, "text": "Mainboard and other CAD I got the mainboard to stack on top of each other with a weird jumper and two piece jumper holder and some screws setup which required some revisions and trying to get everything to fit. I’ve squeezed one of the DC buck modules into between the mainboard, and the second arm segment will hold the two remaining DC buck modules. The bottom mainboard PCB also has 2 smaller buck modules. There’s a lot of things going on with the voltages in this project due to the servos peak current draw being so large. I also refined the arm segment CAD by some reprints and made the DS5180 servo fit better. I’ve also made one of the buck module mounts for the second arm segment to fit more cleanly."}, {"post_id": 22393, "ts": "2026-07-04T21:03:02Z", "hours": 4.3272, "text": "Servo Control Working, More CAD and 3D Printing I got the mainboard to be able to control the servo on the servo PCBs. I also tested a bunch of the functions, like the magnetic sensor, neopixel LED, etc The communication now sends the PCB ID, and the target speed and the servo PCB echos it back and moves the servo. I also worked on the CAD (and refined) for the mainboard connector that will make connecting the two mainboards not a total pain. I also soldered the connectors on the mainboard PCBs for them to connect to each other."}, {"post_id": 21568, "ts": "2026-07-03T16:23:00Z", "hours": 0.4833, "text": "Servo PCB Daisy Chaining Working I plugged in the JST VH and JST 1.25mm wires into the PCBs, and they seem to work! I adjusted the message struct to hold a desired PCB sort of address or ID. When the MCU detects that the ID matches the PCB (by using the solder jumpers at the bottom of each PCB to program the PCB ID), only then it lights up the LED and replies to the main board. The mainboard now loops through IDs and sends a message and waits for a response. I’m very very happy this worked without any really big problems."}, {"post_id": 21540, "ts": "2026-07-03T15:45:12Z", "hours": 3.4667, "text": "Crimped JST VH wires, Connected JST 1.25mm wires, Soldered more PCB connectors Crimping JST VH wires is a ton easier than JST 1.25mm, and after a bunch of failed attempts still, I got some okay crimps that work finally. It took me a long time because I didn’t know that I had to rotate the wire for the hook on the terminal to go into the housing hole, and then also that the box shape part on the terminal actually has to stick out of the crimper or the crimper will crush the whole terminal into bits"}, {"post_id": 21185, "ts": "2026-07-03T00:14:50Z", "hours": 4.7722, "text": "Finished Soldering all Smaller PCBs & Got RS-485 communication working from main board to servo PCBs First, I finished soldering the PCBs. I added the connectors for power and the small data connectors. I also finished the servo expansion boards that hold the magnetic encoder and sensor. I also finished soldering the joint encoder PCBs that will track shoulder and elbow lift servo positions. I also got the bus communication to work. It was really really painful and I was really scared it wouldn’t work. First, the PCBs wouldn’t communicate at all. But eventually, I realized that it was because I wasn’t supplying 3.3V to the main board. This is when I realized that I haven’t adjusted the buck converters on the main board to supply the correct voltage, so I was about to unsolder the buck converter when I realized that I could just set the power supply to 5V (which the ICs on the mainboard can handle luckily), and then adjust the voltage down to 3.3V. But after a really long time of debugging, it still wasn’t working. The connection would sometimes work but was really flaky. Eventually, I soldered on the RPi Pico 2 onto the board completely and then found out the reason, it was because the ATtiny3216 was set to clock at a slow speed that would mess with the communication. I made them use the SerialTransfer library to send data more reliably and it now can communicate properly :)"}, {"post_id": 20354, "ts": "2026-07-01T22:40:54Z", "hours": 5.5597, "text": "Done Soldering Most PCBs, Crimped JST wires kind of I’m pretty much done soldering, I just have like 3 more tiny PCBs and I should be done. The big main board PCBs are pretty much done, I just need to make some botched tall headers since the clearance of regular headers are too small, and the servo PCBs are almost done, with just some connectors left to solder. Soldering JST 1.25mm wires ARE THE WORST EVER THING TO EVER EXIST I spent 2 hours crimping terminals on multiple wires and its so so so annoying. I can barely hold the terminal itself and they are so easy to bend. I couldn’t afford the real tool to crimp them, so I have cheap crimpers that are the wrong size and can’t technically crimp these. If crimping the metal things wasn’t bad enough, inserting the terminals into the housing was even worse. The housing would bend, or I would accidentally somehow insert it where it shorts the pin next to it??? Also while soldering a JST 1.25mm connector on a PCB, I ripped the pads clean off the PCB. I guess that one just will be missing the daisy chain feature because it only has one connector for data now and all the components and MCU are already soldered on, which really sucks. The rest of the servo PCB (4 in total for now) went fine though, and I’m getting faster at it."}, {"post_id": 18843, "ts": "2026-06-30T01:27:03Z", "hours": 4.7572, "text": "Worked on firmware & CAD & Soldered Second PCB I got the PCB and ATtiny3216 to power a servo. I soldered on the remaining components on it, like the custom LED (which just turns on when the motor is spinning for now) and the 100uF capacitor and also the RS485 bus IC. I also changed the CAD of the arm to use a bearing and changed the servo horn to fit inside of the bearing. I’ve ordered right angle headers and the bearing and metal servo horn already. I also added some transparent parts to the arm design to make it look more interesting hopefully. I also am almost done soldering the second servo PCB. Now that I have all the parts ready and I actually got better at soldering ICs I can do it faster now."}, {"post_id": 17022, "ts": "2026-06-27T17:00:47Z", "hours": 4.0433, "text": "Finally got ATtiny3216 on PCB Working This took so so long. Basically, I spent hours trying to get it to work. First, the 3.3V rail wasn’t getting power and the LED wasn’t lighting so I had to resolder the voltage regulator and then both green LEDs lit up, meaning everything was working. Then I got my FTDI programmer, and plugged it into my PCB. Because I have a FTDI programmer and the ATtiny3216 has a UPDI programming interface, there is a resistor on the board to combine the two pins into one. Even just getting megaTinyCore installed in Arduino IDE was a pain. The website that hosted the package info was down again, with an expired certificate. Apparently, this happens very often with it but I found an alternative mirror. Then I tried flashing code, and it didn’t work. Then I realized that the board type I was using was the Optiboot version, and I couldn’t find the non Optiboot version so I had to flash Optiboot onto the MCU. I did not realize that doing this would cause problems later on. I also had to switch the programmer from the one that programs Arduinos to the UPDI programmer. But I STILL couldn’t get any GPIO to work. I just tried things for hours. I switched to PlatformIO. I tried switching from C to Arduino in PlatformIO. I tried unplugging the programmer because I saw some voltage leaking from it. I tried emptying the caps and trying again. I probed every single pin to see if I wrote the wrong pin. Then after all of that, when I switched to non Optiboot (having to find online that I have to downgrade megaTinyCore), and flashing that, it works. I think it was because flashing a bootloader burns some fuses to expect a bootloader, but when I flash my code it actually overwrites the bootloader code because I don’t have access to the TX and RX pins typically used to flash when Optiboot is installed, and that just caused the code to not run."}, {"post_id": 14058, "ts": "2026-06-23T01:15:22Z", "hours": 2.3747, "text": "Soldered Smaller PCBs, Ordered Missing Parts, Continued CAD work I soldered most of the small PCBs, but now I need to order the parts I missed from the first order I placed. I’m also running out of 0.1uF caps, which sucks. I added a spot and holes for a fan on the camera mount and also some arm segment CAD improvements and back plating to hold the metal rods in. I printed out a test and it works fine. As for the PCBs, I’m still waiting for a few parts."}, {"post_id": 13444, "ts": "2026-06-22T00:49:23Z", "hours": 2.6311, "text": "Continued Soldering PCB (Diodes/LEDs, power circuitry, etc) and worked on arm CAD. For the PCB, after some time, I got the power switching circuit to output power. It’s a collection of things that I hope will make it safer, with a some diodes and a fuse, some MOSFETs, so the main MCU can command to send power, and also another stage that requires the E-STOP to not be pressed to actually be able to send power through with two MOSFETs (one main power, another switch the main MOSFET). I added a fan to the global shutter main wrist camera since it gets hot, and added a new “head” to the arm segment CAD (the heads are swappable depending on what you need to print) for the smaller motor for the last DOFs in the arm. It fits my custom daisy chaining two part PCB hat for the MG996R to give it current, temp, power sensing, and daisy chaining power and data via a RS485 bus with an onboard MCU to provide communication and control. I added some holes for airflow for this PCB. I also added some screw holes to actually connect the halves of the arm together."}, {"post_id": 12830, "ts": "2026-06-21T00:22:41Z", "hours": 1.6036, "text": "Continued Soldering and Working on CAD I snapped some headers and soldered on some more ICs on the PCBs. I did also mess up and ordered a wrong footprint for a part, so now I have to delay that too. It’s so tiny I can barely see the pins. I also worked on the CAD, where I designed the stereo camera holder, added a spot for the RPi 5 to go on top, and maybe a display. I also made a V2 of my grabber since the old one didn’t work well. It also seems that Stardance isn’t syncing my time properly, and it’s missing a few hours so that’s annoying :("}, {"post_id": 12281, "ts": "2026-06-20T01:43:23Z", "hours": 5.3406, "text": "I soldered some parts on the PCB, and worked on the CAD files some more I did realize that I forgot some parts, so I ordered them on DigiKey. I’ll continue soldering the rest tomorrow. One of the parts was the LDO, which I only found out because I soldered the parts and realized that the voltage being put on the 3.3V rail is 5V My project needs both versions of the same LDO and I only bought the 5V one I also bought the wrong resistors. I’ve ordered them and they should arrive in a week or so. My design of stacking the PCBs and then adding the buck modules in the middle might be very challenging, and I might need to stack multiple headers on top of each other to fit the modules inside. Hopefully it will still fit inside my arm segment and not short anything. There also needs to be sufficient airflow, which is def a challenge"}, {"post_id": 10584, "ts": "2026-06-17T12:06:26Z", "hours": 1.8508, "text": "Added Bottom Reinforcements with holes for magnets and wiring Added Fans and airflow holes And just fixed and refined the CAD, like adding spots for magnetic attached front and back plates and more"}]...
|
Title: revo by @ratmuds | Stardance
Devlogs:
Adde Title: revo by @ratmuds | Stardance
Devlogs:
Added Bottom Reinforcements with holes for magnets and wiring Added Fans and airflow holes And just fixed and refined the CAD, like adding spots for magnetic attached front and back plates and more
I soldered some parts on the PCB, and worked on the CAD files some more I did realize that I forgot some parts, so I ordered them on DigiKey. I’ll continue soldering the rest tomorrow. One of the parts was the LDO, which I only found out because I soldered the parts and realized that the voltage being put on the 3.3V rail is 5V My project needs both versions of the same LDO and I only bought the 5V one I also bought the wrong resistors. I’ve ordered them and they should arrive in a week or so. My design of stacking the PCBs and then adding the buck modules in the middle might be very challenging, and I might need to stack multiple headers on top of each other to fit the modules inside. Hopefully it will still fit inside my arm segment and not short anything. There also needs to be sufficient airflow, which is def a challenge
Continued Soldering and Working on CAD I snapped some headers and soldered on some more ICs on the PCBs. I did also mess up and ordered a wrong footprint for a part, so now I have to delay that too. It’s so tiny I can barely see the pins. I also worked on the CAD, where I designed the stereo camera holder, added a spot for the RPi 5 to go on top, and maybe a display. I also made a V2 of my grabber since the old one didn’t work well. It also seems that Stardance isn’t syncing my time properly, and it’s missing a few hours so that’s annoying :(
Continued Soldering PCB (Diodes/LEDs, power circuitry, etc) and worked on arm CAD. For the PCB, after some time, I got the power switching circuit to output power. It’s a collection of things that I hope will make it safer, with a some diodes and a fuse, some MOSFETs, so the main MCU can command to send power, and also another stage that requires the E-STOP to not be pressed to actually be able to send power through with two MOSFETs (one main power, another switch the main MOSFET). I added a fan to the global shutter main wrist camera since it gets hot, and added a new “head” to the arm segment CAD (the heads are swappable depending on what you need to print) for the smaller motor for the last DOFs in the arm. It fits my custom daisy chaining two part PCB hat for the MG996R to give it current, temp, power sensing, and daisy chaining power and data via a RS485 bus with an onboard MCU to provide communication and control. I added some holes for airflow for this PCB. I also added some screw holes to actually connect the halves of the arm together.
Soldered Smaller PCBs, Ordered Missing Parts, Continued CAD work I soldered most of the small PCBs, but now I need to order the parts I missed from the first order I placed. I’m also running out of 0.1uF caps, which sucks. I added a spot and holes for a fan on the camera mount and also some arm segment CAD improvements and back plating to hold the metal rods in. I printed out a test and it works fine. As for the PCBs, I’m still waiting for a few parts.
Finally got ATtiny3216 on PCB Working This took so so long. Basically, I spent hours trying to get it to work. First, the 3.3V rail wasn’t getting power and the LED wasn’t lighting so I had to resolder the voltage regulator and then both green LEDs lit up, meaning everything was working. Then I got my FTDI programmer, and plugged it into my PCB. Because I have a FTDI programmer and the ATtiny3216 has a UPDI programming interface, there is a resistor on the board to combine the two pins into one. Even just getting megaTinyCore installed in Arduino IDE was a pain. The website that hosted the package info was down again, with an expired certificate. Apparently, this happens very often with it but I found an alternative mirror. Then I tried flashing code, and it didn’t work. Then I realized that the board type I was using was the Optiboot version, and I couldn’t find the non Optiboot version so I had to flash Optiboot onto the MCU. I did not realize that doing this would cause problems later on. I also had to switch the programmer from the one that programs Arduinos to the UPDI programmer. But I STILL couldn’t get any GPIO to work. I just tried things for hours. I switched to PlatformIO. I tried switching from C to Arduino in PlatformIO. I tried unplugging the programmer because I saw some voltage leaking from it. I tried emptying the caps and trying again. I probed every single pin to see if I wrote the wrong pin. Then after all of that, when I switched to non Optiboot (having to find online that I have to downgrade megaTinyCore), and flashing that, it works. I think it was because flashing a bootloader burns some fuses to expect a bootloader, but when I flash my code it actually overwrites the bootloader code because I don’t have access to the TX and RX pins typically used to flash when Optiboot is installed, and that just caused the code to not run.
Worked on firmware & CAD & Soldered Second PCB I got the PCB and ATtiny3216 to power a servo. I soldered on the remaining components on it, like the custom LED (which just turns on when the motor is spinning for now) and the 100uF capacitor and also the RS485 bus IC. I also changed the CAD of the arm to use a bearing and changed the servo horn to fit inside of the bearing. I’ve ordered right angle headers and the bearing and metal servo horn already. I also added some transparent parts to the arm design to make it look more interesting hopefully. I also am almost done soldering the second servo PCB. Now that I have all the parts ready and I actually got better at soldering ICs I can do it faster now.
Done Soldering Most PCBs, Crimped JST wires kind of I’m pretty much done soldering, I just have like 3 more tiny PCBs and I should be done. The big main board PCBs are pretty much done, I just need to make some botched tall headers since the clearance of regular headers are too small, and the servo PCBs are almost done, with just some connectors left to solder. Soldering JST 1.25mm wires ARE THE WORST EVER THING TO EVER EXIST I spent 2 hours crimping terminals on multiple wires and its so so so annoying. I can barely hold the terminal itself and they are so easy to bend. I couldn’t afford the real tool to crimp them, so I have cheap crimpers that are the wrong size and can’t technically crimp these. If crimping the metal things wasn’t bad enough, inserting the terminals into the housing was even worse. The housing would bend, or I would accidentally somehow insert it where it shorts the pin next to it??? Also while soldering a JST 1.25mm connector on a PCB, I ripped the pads clean off the PCB. I guess that one just will be missing the daisy chain feature because it only has one connector for data now and all the components and MCU are already soldered on, which really sucks. The rest of the servo PCB (4 in total for now) went fine though, and I’m getting faster at it.
Finished Soldering all Smaller PCBs & Got RS-485 communication working from main board to servo PCBs First, I finished soldering the PCBs. I added the connectors for power and the small data connectors. I also finished the servo expansion boards that hold the magnetic encoder and sensor. I also finished soldering the joint encoder PCBs that will track shoulder and elbow lift servo positions. I also got the bus communication to work. It was really really painful and I was really scared it wouldn’t work. First, the PCBs wouldn’t communicate at all. But eventually, I realized that it was because I wasn’t supplying 3.3V to the main board. This is when I realized that I haven’t adjusted the buck converters on the main board to supply the correct voltage, so I was about to unsolder the buck converter when I realized that I could just set the power supply to 5V (which the ICs on the mainboard can handle luckily), and then adjust the voltage down to 3.3V. But after a really long time of debugging, it still wasn’t working. The connection would sometimes work but was really flaky. Eventually, I soldered on the RPi Pico 2 onto the board completely and then found out the reason, it was because the ATtiny3216 was set to clock at a slow speed that would mess with the communication. I made them use the SerialTransfer library to send data more reliably and it now can communicate properly :)
Crimped JST VH wires, Connected JST 1.25mm wires, Soldered more PCB connectors Crimping JST VH wires is a ton easier than JST 1.25mm, and after a bunch of failed attempts still, I got some okay crimps that work finally. It took me a long time because I didn’t know that I had to rotate the wire for the hook on the terminal to go into the housing hole, and then also that the box shape part on the terminal actually has to stick out of the crimper or the crimper will crush the whole terminal into bits
Servo PCB Daisy Chaining Working I plugged in the JST VH and JST 1.25mm wires into the PCBs, and they seem to work! I adjusted the message struct to hold a desired PCB sort of address or ID. When the MCU detects that the ID matches the PCB (by using the solder jumpers at the bottom of each PCB to program the PCB ID), only then it lights up the LED and replies to the main board. The mainboard now loops through IDs and sends a message and waits for a response. I’m very very happy this worked without any really big problems.
Servo Control Working, More CAD and 3D Printing I got the mainboard to be able to control the servo on the servo PCBs. I also tested a bunch of the functions, like the magnetic sensor, neopixel LED, etc The communication now sends the PCB ID, and the target speed and the servo PCB echos it back and moves the servo. I also worked on the CAD (and refined) for the mainboard connector that will make connecting the two mainboards not a total pain. I also soldered the connectors on the mainboard PCBs for them to connect to each other.
Mainboard and other CAD I got the mainboard to stack on top of each other with a weird jumper and two piece jumper holder and some screws setup which required some revisions and trying to get everything to fit. I’ve squeezed one of the DC buck modules into between the mainboard, and the second arm segment will hold the two remaining DC buck modules. The bottom mainboard PCB also has 2 smaller buck modules. There’s a lot of things going on with the voltages in this project due to the servos peak current draw being so large. I also refined the arm segment CAD by some reprints and made the DS5180 servo fit better. I’ve also made one of the buck module mounts for the second arm segment to fit more cleanly.
Bidirectional Communication To PC/RPi 5 with LeRobot & More CAD changes I setup a basic class for my robot in LeRobot (which was a little confusing) and then I also got communication between the Pico and my PC working. Originally I have SPI pins exposed on my mainboard PCB to connect to the RPi 5’s GPIO pins, but for some reason after lots of debugging, even running it at super low speeds it just would not work. So I switched to just using serial over USB, and using the SerialTransfer library to send data more reliably. The script on the PC now can send target joint angles, and receive a lot of data back, like if the joint is online, temp, current, angle, homing sensor, home status, LED, firmware version, voltage, etc I also iterated on the CAD for everything, and it should be done soon. It’s just a bunch of tolerance fixes to make everything fit at this point and some new things like mounts for encoders and DC buck modules and fixing the CAD for the custom MG996R case.
Rewrote all firmware; Fixing Servo Cover and a ton of other CAD; Almost fried everything For the CAD, it doesn’t look like much changed, but I’ve basically redesigned a bunch of things and fixed tolerances and now it should move really smoothly. With the bearings added, everything moves incredibly smoothly now Especially the servo cover was annoying. I needed to attach a magnet to the shaft which was really annoying. I haven’t logged that well, I’ve done more like 15 hours of work instead of just 5 but whatever Most of those hours was the painful debugging. RS-485 busses, JSON not communcating with the PC, dropped connections, corrupted data, disconnected jumpers, FTDI programmer becoming flaky for some reason It’s just been pain :((( Now the PC (screenshot) recieves all the data and sends all the data You can see a lot (not all) of the telemetry being received. Ignore the volts though my math is wrong. Realtime connecting now works smoothly, as you can see the other RS485 servos are offline.
Servo firmware almost done The servo firmware now (with the mainboard) sends validation data back and forth so both the servo and mainboard can verify that the data was sent successfully and can ignore corrupted packets because my wiring can be flaky sometimes The firmware now sends real data again and is much more robust, with a proper stateful LED that shows useful information, like offline, online and driving motor, homing, error (fast flashing of LED as red), and the error state is also triggered in the case of corrupted packets (I tried it and it works :)) I’m also still printing out the pieces to assemble the arm. I actually have more hours working on it, I just forgot to log :(((((((
Finalizing hardware I crimped and soldered almost the remaining wires, I’ve made some last redesigns to the arm CAD and I’m now fitting everything into the new prints and assembling. I’ve also had a ton of trouble with my servo PCBs lately, but I found out a lot of the issue was my programmer is actually fried somehow, so I’m now using an old RPi Pico to act as a UPDI adapter so PlatformIO can now successfully program the ATtinys much better now. A lot of the CAD was slightly tweaked so that everything fits better
Test assembly and fitting of robot arm I am mostly done with the core functionality of the mainboard, so i packed everything together and double checked eerything works including power Then I started assembling most of the servos The smaller servo casing doesn’t fit very well, and the transparent window doesn’t fit either But the magnetic cover for the arm segments works great :) It was a lot of fitting, screwing, testing with multimeter, etc
Mainboard Stack Redo & Firmware Updates and Test Web UI I had to take the electronics out, and redo some of the wiring. The screw terminals are pretty cheap and suck, which makes it incredibly annoying to use as it’s impossible to fit these thicker AWG wires in and they sometimes slide out of the terminals. When I’m done I’m probably going to hot glue the wires. The incredibly fragile flexible extension jumpers for data transfer between the stack is also super fragile, and especially doesn’t grip. Sometimes the data wires just slip out. I took apart the arm, as during testing the motor stripped some screws. I’ve ordered some heat set inserts to prevent that from happening again, which will require me to reprint all the arm segments again. I also added a simple web UI for moving the joints. There is an empty ThreeJS sidebar and the right has some graphs (real data) from the mainboard sensors. The smaller MG996R servos haven’t been mounted yet due to a power delivery issue from the mainboard but hopefully that will be fixed soon.
Assembled Basic Arm Parts and Packaged Main Board Assembly; Reprogrammed servo MCUs & worked on web UI I redid some soldering on the mainboard PCB, secured connections, and secured everything with tape for now. I’ll likely have to reprint the first arm segment to fit everything in flush because as you can see there are wires and things bulging out of the segment itself right now. I also redesigned the CAD to include heat set inserts. I did programming and a bunch of bug testing and fixing too. An hour of time was lost because Lapse errored out :((((( very sad about that :( I also had an issue with data corruption on the RS485 bus but that is fixed now, and it works like it should.
New Mainboard Cover, Wiring, Bearing Revamp, Heatset Inserts, Servo Cover I added space for the mainboard segment, now it has clearance to actually fit, I redid the cover for this segment, and now it can allow the wires to come out The servo mount now has heatset inserts, because the previous screw in print kept slipping out, but this time the screws just would not go into the inserts through the mount for some reason? The screws went in normally, but with the tolerances, when multiple screws go in they just fight and push against the edge of the insert instead of going in. The MG996R servo housing now has been redesigned, based off of picture references and calipers to attempt to be more accurate than the last one to try and reduce the amount of bad tolerances affecting the gears and making them misaligned, reducing torque output. And just general wiring and CAD work.
Heatset Inserts Working; Fixing Faulty Motor Power Connections; Internal Fan Power Connectors I verified that the servo cover works, and it’s much much better than the last version. The tolerances are much better this time, leading to less stress on the gears. The same amount of turning uses around 100mA compared to 300mA before, almost reaching it’s limit without any load. Some motor power connectors disconnected, so I found it with a multimeter and added hot glue to some connectors to secure them. I soldered the fan power things inside the PCB, but realized that the JST connectors I’m using are different from the fans. I might have to make a sort of adapter or something by crimping more wires. And the heatset inserts finally work after some tweaking, before the inserts would get misaligned when put in, now with a aligning lip thing it can be slotted in and melted in more accurately.
Reprinted Arm Segments; Added Segment Cover; Servo Mount Working; Working on Web UI I reprinted the arm segments, hopefully for one of the last times, I’ve went through so much filament already. It now allows the mainboard to be neatly packed inside, hiding most of it while still allowing wires to come out. Due to the design of the mainboard, the wires do have to stick out the front like that sadly. To avoid crushing wires, the servo mount has been expanded to allow more clearance for wires and general electronics. The servo mount works now with a new design. I remeasured and adjusted the sketches and added proper hole sizes and a little chamfer to allow the insert to slot in before using a soldering iron to melt it in to help align much better than before. The web UI now shows a representation of the arm. It’s not tuned yet, so the angles are wrong but I’ll fix it soon. I had to move everything from Fusion to Onshape to create the assemblies, and then use a Python library to convert the Onshape assembly into a URDF file that can be rendered with the correct joints inside ThreeJS. Next, I’ll be adding IK and general arm control.
Working on IK System & Resoldered Joints Again Bruh so my robot arm after finally being assembled by the way exceeded it’s limits and tore its wires out. I had to take apart the mainboard assembly again, which just is SO TEDIOUS. I just dumped solder and hot glue again and fixed a short caused from another wire being sheared off. Also I’m working on the IK system, but it’s not really going well right now. I need to add offsets and config to each of the motors to get them to work correctly. The motors hooked up right now started crashing into my table sadly. Hopefully it will work soon. I also continued working on the servo case. I still haven’t added the code to calibrate and get closed loop control on the MG996R servos. The DS5180 encoders should be easier though.
V2 Servo Case Working; Servo Code Revamp WIP After around 10 more revisions, the new servo case is fully working with the magnetic extender. The V2 is significantly better designed and higher efficiency than the last, more guess and check result. I’ve also started to rewrite the servo code. Right now, its really messy and just kind of snowballed over time as I’ve slapped on patches and features to a fundamentally weak base. I’m in the progress of writing that now, hopefully it will be done soon. It will also include the new auto calibration and encoder usage and PID (and potentially advanced auto tuned curve to combat motor acceleration overshoot and backlash)
Servo Code I did some slight refining to the servo case again, making the tolerances tighter to keep the gears from wobbling. It’s still sort of loose, so I need to just keep trying over and over again. I’m also still writing the servo code. It’s already much more organized than before. I’m also learning Vim keybinds and stuff to try and code faster. The RS485 bus communication works now, and it’s just sending dummy telemetry data back right now. It can also control the servo fine. I’ve just been doing like the lower level architecting I guess, or not really I’m not sure. Before everything was crammed into a single file and now there’s actually header files and multiple source files and it’s actually somewhat readable. I had an issue with LED control for a second but that’s fixed now :)
MG996Rs Finally Working The servos finally work. I don’t even think this amount of hours logged is equal to how long this took, because I just got tired of it. I think I printed like 20 new revisions of the case and servo mount. It was just so so annoying. While the general case worked, getting a reliable magnet output shaft was impossible. From tolerances, to my own errors to binding to hot glue getting everywhere, or superglue binding random gears, or my getting my fingers stuck to the actual case from superglue (was not fun and hurt) The case finally works. And the prototype code somewhat works too. It basically rotates until the sensor hits zero, it resets the “ticks”. A tick is a raw output from the AS5600. Because this magnet shaft spins faster than the output shaft, like 10x faster, the MCU needs to poll the sensor super quickly, which also needed some optimizations to clear the blocking bus communication wait that would let the magnet skip multiple full turns. The sensor doesn’t internally count turns so we just need to count manually and detect when it jumps from 4096-0 or the other way around. It then spins around until the sensor is hit AGAIN to calculate tick to degrees, assuming the sensor was zeroed before, and then the current ticks would equal 360 degrees. Another problem was the servo needs a PWM signal to move. The threshold from stop to go is pretty garbage on these cheap servos, leading it to spin quite fast already when at the lowest PWM value. Instead when far from the value it increases the PWM signal, closer it brings to minimum and starts linearly flickering the PWM signal from 0 to the speed, with the closer it is the more ratio of time stopped to inch it closer to the target. It actually surprisingly works. It’s very fragile though, any skipped reads, from potentially slow bus transfers would completely throw it off, so all the code running locally on the weak 8 bit MCU needs to be extremely lightweight. Yeah this was super super painful. I’ve been sitting here redoing superglue and code for hours.
Debugging and Fixing Is Killing Me and the Mainboard Exploded :( I have spent SO many hours trying to get this to work. It’s so annoying. This isn’t near how many hours I spent, I’ve been too frustrated to log it. The case is incredibly annoying. Getting the initial case works fine. Doing all of this stuff makes it complicated and break and jam up. The RS485 busses just randomly stop working. The servo output is wiggly and not stable. A PCB header snapped and I didn’t have replacements so I had to bodge solder it back on. The mainboard voltage regulator literally exploded. It was a cheap part but yeah that literally just broke stuff. Fixes to everything. CAD. Firmware. bruh
Dashboard Revamp WIP, Tons of CAD changes, Code Updates, Tolerances I’ve just been refining everything and it’s just going really slowly. Many problems. Magnets shorting out stuff. Magnet too far and not being detected. Wires breaking. I’ve also redesigned the dashboard UI to hopefully look better. I’m still working on it, hopefully it will look more polished later. The dashboard It shows a load speedometer type thing, with the load (max current from a motor out of 500mA) and the temp on the right (for now just dummy value) The motors have cool little animations now too and look less cluttered. I’ve just been working on it a lot. Just everything in general tbh.
Assembled last 4 DoF for Integrated Test I assembled the MG996R servos in a test piece that will represent the full regular arm segment to test if everything worked. With some fiddling it worked. As you can see there’s more transparent pieces. The pieces that connect the previous joint are transparent to show the Neopixel below it a lot better as before the LED was almost completely occluded. The bright green LEDs power status ones are too bright but I can cover them later.
Significantly Better Motor Control and Gripper CAD The motor code was getting a bit messy, so I rewrote it. I separated the motor code from the main control code which cleaned it up a bit. LADRC I’m using LADRC now. It’s kind of like PID, but advanced. I took the implementation from online. I feed it the motor current, encoder value, and the target and it goes. The algorithm or whatever somehow calculates the velocity of the motor and how much “effort” it’s using and then gives an effort to put to the motor. It somehow cancels out like disturbances like gravity, backlash, the general physics, etc. As you can see, the motor control is so so so much better than before. It cleanly decelerates. It handles load and dynamically increases effort. Before there was some oscillation because it just couldn’t figure out how to get to the target, this one is just so much better and so hands off. There’s basically like no calibration as it does it over time :)))
Assembled Full Arm (not all motors active yet for safety) and Basic Interpolation for Control I assembled the new full arm assembly minus the end effector and didn’t plug in the first two motors as they have the highest chance of doing something really bad. I also made some general changes to the code and also added a flag to some motors to interpolate their movement slowly to not explode the robot arm while controlling it.
Fixed Broken Magnet, Redoing all IK joint data and offsets, Stereo depth test An encoder magnet broke off, so I had to disassemble and reassemble the top half of the arm to get to the encoder magnet and glue it back on. I also started properly punching in the IK joint data, before it was mostly just random data but now almost all of the data is mostly correct, and I’m waiting for a print to complete that will flip a motor to finally get the IK simulation matching with real life and have the IK endpoint be tracked at the tip of the gripper. It was a lot of trial and error and measurements. I also started experimenting with adding depth estimation. Eventually this could add collision avoidance IK pathfinding, which would be really cool but it’s just a pointcloud in the render right now. But the robot arm is slowly coming together finally! I’ll be able to grab something soon I think.
Integration Hell I’ve just been like trying to get this stupid project to work. Screws keep falling out, the IK keeps failing, things are hitting each other and wires are being strained I’m in integration hell. Everything works fine on its own, but combining everything is horrible. I’ve been reassembling and trying to fix this stupid thing for so long. Anyway I’ve went through a few more revisions of some CAD and some more refinements and the gripper and camera is now mounted but this is just incredibly incredibly annoying :((((
Setup VR and Gripper, Robot Rapidly Disassembles my robot arm is broken I setup VR and fixed some more IK when I go into VR and try playing around, it instantly flies out and throws itself off the table and breaks into pieces. This is going to be fun to fix.
Finish Project Up :) I wrote a ton in the repository im tired :( https://github.com/ratmuds/revo
Ships:
I wanted to make a robot arm that punched above it’s weight. The price is pretty similar to smaller robot arms on Amazon that may not include cameras, which would need to be purchased seperately. The way this is achieved by using cheap servos, servos that are 100% NOT made for this purpose, and adding most of the functionality of a smart servo back by using a TON of PCBs. It was a lot of soldering. I just wanted to make something that had more features for less money :) This project was SUPER difficult for me, and pushed my limits a bit. The amount of tolerences especially for the stupid custom motor case and magnet and PCBs and the crazy mainboard and generally having everything play well together took so long, I had many days where I quit frustrated. I’m very happy about how it turned out though, even seeing the lots of shortcomings it has....
|
1792853855.2155955
|
1792940255.2155955
|
|
29121
|
hivemind by @justin0711 | Stardance
|
justin0711
|
0
|
134.77
|
0
|
[]
|
[{"post_id": 46864, "ts": "202 [{"post_id": 46864, "ts": "2026-08-14T12:22:31Z", "hours": 20.5064, "text": "We’re developing a Rust sandbox, adding a bunch of features to make it function like a programming language."}, {"post_id": 45532, "ts": "2026-08-12T02:26:01Z", "hours": 15.4608, "text": "I wrote a detailed document for the official website and also wrote a sandbox executor in Rust, which I think has excellent performance and usability."}, {"post_id": 37401, "ts": "2026-07-30T02:49:53Z", "hours": 9.3197, "text": "The previous front-end was merged, and a preliminary sandbox interpreter was written, which has the functions of conditional variable function operations and conditional judgment .."}, {"post_id": 33256, "ts": "2026-07-23T00:50:53Z", "hours": 15.6447, "text": "Fixed the issue where the master could not view logs correctly, deployed nodepool and VPN services, and included the VPN client in the installation package."}, {"post_id": 29339, "ts": "2026-07-16T06:51:50Z", "hours": 2.1697, "text": "write the official website and fixed some previous architectural issues. changed the task ID to UUID."}, {"post_id": 29289, "ts": "2026-07-16T03:14:02Z", "hours": 71.6733, "text": "The entire system has been refactored using Rust, and the underlying sandbox has been replaced."}]...
|
Title: hivemind by @justin0711 | Stardance
Devlog Title: hivemind by @justin0711 | Stardance
Devlogs:
The entire system has been refactored using Rust, and the underlying sandbox has been replaced.
write the official website and fixed some previous architectural issues. changed the task ID to UUID.
Fixed the issue where the master could not view logs correctly, deployed nodepool and VPN services, and included the VPN client in the installation package.
The previous front-end was merged, and a preliminary sandbox interpreter was written, which has the functions of conditional variable function operations and conditional judgment ..
I wrote a detailed document for the official website and also wrote a sandbox executor in Rust, which I think has excellent performance and usability.
We’re developing a Rust sandbox, adding a bunch of features to make it function like a programming language....
|
1792540213.9539003
|
1793145013.9539003
|
|
19478
|
Searchuse 1.4.4 by @ai_zamden | Stardance
|
ai_zamden
|
3
|
135.0
|
1486
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-07T22:47:37Z", "hours": 33.0, "multiplier": 11.36, "payout": 340, "blessing": "neutral", "text": "Hey guys, this is my third ship for my turn-based strategy game Searchuse. A lot of you guys suggested to add a tutorial, so I made one, and now it’s a lot easier to figure out how to play the game! I hope you enjoy.", "repo": "https://github.com/aidenzma/Searchuse", "demo": "https://ai-zamden.itch.io/searchuse"}, {"ship_num": 2, "ts": "2026-07-17T01:35:02Z", "hours": 77.0, "multiplier": 14.75, "payout": 853, "blessing": "neutral", "text": "I’ve listened to the people and have tried to make as many improvements as I could. UI has been improved Art has been pixel-adjusted Controls are specified in the README Main Menu button has been added Searchuse is now available on Linux AND Searchuse is now available on BROWSER (itch.io, the demo link) So now everyone is theoretically able to try it! A large portion of my time was spent creating a neural network for the bot (that can only be experienced in Train Bot, there’s a hardcoded bot and a neural network bot.) (The neural network bot is still very inexperienced currently.) (Admittedly, Gemini helped me a lot with setting up that portion of the game.) For specific help in downloading the zips, visit the README.", "repo": "https://github.com/aidenzma/Searchuse", "demo": "https://ai-zamden.itch.io/searchuse"}, {"ship_num": 3, "ts": "2026-06-25T04:03:09Z", "hours": 25.0, "multiplier": 12.05, "payout": 293, "blessing": "neutral", "text": "I made a turn-based strategy game called Searchuse, where you search for and then use various items with different mechanics. A challenge in the process was getting all of the turn-based logic right. (For cooldowns and lasting effects, the timing can be surprisingly complicated to get down.) I’m proud that there aren’t any glaring bugs in the game right now. To play, move around using arrow keys, and press the buttons search or use. There are deeper mechanics you can learn, but the surface-level controls should be intuitive! Here’s the trailer: https://youtu.be/wL6j5aslFXA Join the Discord: https://discord.gg/NpBV6vHc7", "repo": "https://github.com/aidenzma/Searchuse", "demo": "https://ai-zamden.itch.io/searchuse"}]...
|
[{"post_id": 63898, "ts": "202 [{"post_id": 63898, "ts": "2026-09-07T22:44:08Z", "hours": 3.3097, "text": "I’m finished with the tutorial, so now you guys can easily learn how to play Searchuse!"}, {"post_id": 63059, "ts": "2026-09-06T17:21:51Z", "hours": 9.2094, "text": "I’m making a tutorial to teach new users how to play the game, as requested by feedback for my latest ship."}, {"post_id": 36775, "ts": "2026-07-29T01:24:16Z", "hours": 12.9503, "text": "I did a massive overhaul of the inner workings of the inventory system, changing the data tracking from only ints representing the itemIDs to structs which provide a container for data of items. I did this because I had to add individual item durability."}, {"post_id": 34389, "ts": "2026-07-25T02:19:17Z", "hours": 7.4644, "text": "After a long while, some new content is finally added to Searchuse. Introducing: Armor Piercing Rounds: Use with certain weapons to allow damage to be done as if the opponent were wearing no armor! Beretta+: An upgraded version of the Beretta, this weapon allows you to do damage the the enemy’s max health, a groundbreaking new mechanic added in this update. Fixed: Energy Orb edge bug: Now, the Energy Orb and other explosives will explode when hitting the edge of the map instead of not doing anything! Smoke vision bug: A small error in FOV rendering while in a smoked area was fixed. Factory edge bug: A visual glitch that resulted in Factory tiles peeking out from under the greyed out vision area was fixed."}, {"post_id": 29881, "ts": "2026-07-17T01:27:03Z", "hours": 1.3161, "text": "Searchuse is now available on Linux and Browser!"}, {"post_id": 29302, "ts": "2026-07-16T04:03:09Z", "hours": 7.9944, "text": "I’ve made major UI improvements to the game!"}, {"post_id": 26990, "ts": "2026-07-12T03:45:34Z", "hours": 10.4197, "text": "I’ve set up the system so that my hardcoded bot can train the neural network bot. This is gonna be fun!"}, {"post_id": 26063, "ts": "2026-07-10T17:29:06Z", "hours": 8.1575, "text": "I started building a neural network for the bot so that I can train it and it can learn. This is what the data file looks like for all of the weights and nodes."}, {"post_id": 24880, "ts": "2026-07-08T18:45:45Z", "hours": 2.7314, "text": "I coded an algorithm that allows the bot to scan the map, and the resulting data lists are represented here by colors! Red = near mountains & hills Green = near cover (caves & trees) Blue = near loot (buildings)"}, {"post_id": 24522, "ts": "2026-07-08T03:50:22Z", "hours": 15.1186, "text": "I added a world map to Searchuse!"}, {"post_id": 21711, "ts": "2026-07-03T20:06:43Z", "hours": 7.6906, "text": "I’ve upgraded the bot’s intelligence. Instead of only being aware of the enemy when they are in its immediate vision, the bot now remembers where it previously saw the enemy, keeps track of how many opportunities to move the enemy had since then, and precisely calculates all the squares the enemy could be on using a flood fill algorithm, based on how many moves the bot thinks the enemy had. (The eye icons represent what the bot thinks are the possible positions of my player based on where it last saw my player. (Sometimes the player can “escape” the eyes by having more moves than what the bot thinks the player has.) (Otherwise, if the bot is right about how many moves the player has, the player should not be able to move to a square without an eye icon.))"}, {"post_id": 20077, "ts": "2026-07-01T16:24:44Z", "hours": 23.2564, "text": "I worked on the enemy AI for my turn-based game Searchuse. The enemy can now use tools, such as invisibility potions, binoculars, radars, UAVs, and traps like landmines or C4s, and can also get airdrops."}, {"post_id": 15456, "ts": "2026-06-25T03:54:38Z", "hours": 4.6358, "text": "I added a few final features to match my turn-based strategy on Unity to its Scratch predecessor. Added: Hitmarker: When you’re hit with an unsuppressed weapon or bomb, a red arrow appears, telling you in which direction on your player’s character’s body you felt the impact. Cooldown visualiser: When you press c, a visual will show up that will show you all the items you have on cooldown and how many turns it is before you can use them next. 4 turn heavy weapons rule: In the first 4 turns, it is now impossible to find any weapon that totals a damage greater than 25 per turn. 3 weapon limit: You can only carry 3 weapons at a time. (By weapons I mean those that stay in your inventory, so something like Grenade doesn’t count.) If you find another weapon while you’re at your limit, you can choose to switch. Turns text: You can now press t to show the turns text, which is below the moves text and shows how many turns it’s been. Turn indicator: On the bottom of your screen, you’ll now find a green or blue landscape that indicates whose turn it is. (Green meaning your turn, blue meaning the enemy’s turn.)"}, {"post_id": 14868, "ts": "2026-06-24T04:17:21Z", "hours": 2.3808, "text": "Added: Message in a Floating Bottle: Like a book, it gives you a random powerup, but its rarity is lower than that of a book. However, its upside is that it is granted immediately after searching. Can only be found in water. AK-47: The world’s most iconic rifle is now in Searchuse! With 2 shots of 20 damage each, this addition provides more mid-tier weapon choices. AK-74: An upgrade of the AK-47, this gun matches the damage done by the M4A1, but it has a lesser cooldown, making it the more desirable option. Meat Stick: This item provides a solid mid-tier healing option, giving you 40 health in one bite. Spikes: This item gives you three more moves, but takes up an armor slot and provides 0 armor. (my beautiful art below)"}, {"post_id": 14567, "ts": "2026-06-23T18:50:46Z", "hours": 10.5353, "text": "I added a bunch of features, a new item, and fixed some bugs for my turn-based strategy game Searchuse. Added: Replay for when Vital Signs run out Replay for when Binoculars runs out Book: when read, gives you a random skill. These skills range from increasing moves to getting 2 searches per turn, so put on your reading glasses and pick up a book! You can also now press b to see your book inventory. Use arrow keys to navigate. Roll Chances Information: press L to open Roll Chances Information, which will tell you the chances of getting each rarity on each tile when you press search. Use arrow keys to navigate. Changed: Sound when picking up Thermal Vision Goggles Fixed: Not being able to start your turn on bot mode glitch Normalized speeds for UAV animation, texts, and movement"}, {"post_id": 11694, "ts": "2026-06-19T01:46:35Z", "hours": 7.0461, "text": "I added an airdrop to my turn-based strategy game Searchuse, which, instead of one, gives you three options to choose from when you press search."}, {"post_id": 9970, "ts": "2026-06-16T14:36:33Z", "hours": 0.32, "text": "I added a UAV to my turn-based strategy game Searchuse, which tracks down the enemy."}]...
|
Title: Searchuse 1.4.4 by @ai_zamden | Stardance
Title: Searchuse 1.4.4 by @ai_zamden | Stardance
Devlogs:
I added a UAV to my turn-based strategy game Searchuse, which tracks down the enemy.
I added an airdrop to my turn-based strategy game Searchuse, which, instead of one, gives you three options to choose from when you press search.
I added a bunch of features, a new item, and fixed some bugs for my turn-based strategy game Searchuse. Added: Replay for when Vital Signs run out Replay for when Binoculars runs out Book: when read, gives you a random skill. These skills range from increasing moves to getting 2 searches per turn, so put on your reading glasses and pick up a book! You can also now press b to see your book inventory. Use arrow keys to navigate. Roll Chances Information: press L to open Roll Chances Information, which will tell you the chances of getting each rarity on each tile when you press search. Use arrow keys to navigate. Changed: Sound when picking up Thermal Vision Goggles Fixed: Not being able to start your turn on bot mode glitch Normalized speeds for UAV animation, texts, and movement
Added: Message in a Floating Bottle: Like a book, it gives you a random powerup, but its rarity is lower than that of a book. However, its upside is that it is granted immediately after searching. Can only be found in water. AK-47: The world’s most iconic rifle is now in Searchuse! With 2 shots of 20 damage each, this addition provides more mid-tier weapon choices. AK-74: An upgrade of the AK-47, this gun matches the damage done by the M4A1, but it has a lesser cooldown, making it the more desirable option. Meat Stick: This item provides a solid mid-tier healing option, giving you 40 health in one bite. Spikes: This item gives you three more moves, but takes up an armor slot and provides 0 armor. (my beautiful art below)
I added a few final features to match my turn-based strategy on Unity to its Scratch predecessor. Added: Hitmarker: When you’re hit with an unsuppressed weapon or bomb, a red arrow appears, telling you in which direction on your player’s character’s body you felt the impact. Cooldown visualiser: When you press c, a visual will show up that will show you all the items you have on cooldown and how many turns it is before you can use them next. 4 turn heavy weapons rule: In the first 4 turns, it is now impossible to find any weapon that totals a damage greater than 25 per turn. 3 weapon limit: You can only carry 3 weapons at a time. (By weapons I mean those that stay in your inventory, so something like Grenade doesn’t count.) If you find another weapon while you’re at your limit, you can choose to switch. Turns text: You can now press t to show the turns text, which is below the moves text and shows how many turns it’s been. Turn indicator: On the bottom of your screen, you’ll now find a green or blue landscape that indicates whose turn it is. (Green meaning your turn, blue meaning the enemy’s turn.)
I worked on the enemy AI for my turn-based game Searchuse. The enemy can now use tools, such as invisibility potions, binoculars, radars, UAVs, and traps like landmines or C4s, and can also get airdrops.
I’ve upgraded the bot’s intelligence. Instead of only being aware of the enemy when they are in its immediate vision, the bot now remembers where it previously saw the enemy, keeps track of how many opportunities to move the enemy had since then, and precisely calculates all the squares the enemy could be on using a flood fill algorithm, based on how many moves the bot thinks the enemy had. (The eye icons represent what the bot thinks are the possible positions of my player based on where it last saw my player. (Sometimes the player can “escape” the eyes by having more moves than what the bot thinks the player has.) (Otherwise, if the bot is right about how many moves the player has, the player should not be able to move to a square without an eye icon.))
I added a world map to Searchuse!
I coded an algorithm that allows the bot to scan the map, and the resulting data lists are represented here by colors! Red = near mountains & hills Green = near cover (caves & trees) Blue = near loot (buildings)
I started building a neural network for the bot so that I can train it and it can learn. This is what the data file looks like for all of the weights and nodes.
I’ve set up the system so that my hardcoded bot can train the neural network bot. This is gonna be fun!
I’ve made major UI improvements to the game!
Searchuse is now available on Linux and Browser!
After a long while, some new content is finally added to Searchuse. Introducing: Armor Piercing Rounds: Use with certain weapons to allow damage to be done as if the opponent were wearing no armor! Beretta+: An upgraded version of the Beretta, this weapon allows you to do damage the the enemy’s max health, a groundbreaking new mechanic added in this update. Fixed: Energy Orb edge bug: Now, the Energy Orb and other explosives will explode when hitting the edge of the map instead of not doing anything! Smoke vision bug: A small error in FOV rendering while in a smoked area was fixed. Factory edge bug: A visual glitch that resulted in Factory tiles peeking out from under the greyed out vision area was fixed.
I did a massive overhaul of the inner workings of the inventory system, changing the data tracking from only ints representing the itemIDs to structs which provide a container for data of items. I did this because I had to add individual item durability.
I’m making a tutorial to teach new users how to play the game, as requested by feedback for my latest ship.
I’m finished with the tutorial, so now you guys can easily learn how to play Searchuse!
Ships:
I made a turn-based strategy game called Searchuse, where you search for and then use various items with different mechanics. A challenge in the process was getting all of the turn-based logic right. (For cooldowns and lasting effects, the timing can be surprisingly complicated to get down.) I’m proud that there aren’t any glaring bugs in the game right now. To play, move around using arrow keys, and press the buttons search or use. There are deeper mechanics you can learn, but the surface-level controls should be intuitive! Here’s the trailer: https://youtu.be/wL6j5aslFXA Join the Discord: https://discord.gg/NpBV6vHc7
I’ve listened to the people and have tried to make as many improvements as I could. UI has been improved Art has been pixel-adjusted Controls are specified in the README Main Menu button has been added Searchuse is now available on Linux AND Searchuse is now available on BROWSER (itch.io, the demo link) So now everyone is theoretically able to try it! A large portion of my time was spent creating a neural network for the bot (that can only be experienced in Train Bot, there’s a hardcoded bot and a neural network bot.) (The neural network bot is still very inexperienced currently.) (Admittedly, Gemini helped me a lot with setting up that portion of the game.) For specific help in downloading the zips, visit the README.
Hey guys, this is my third ship for my turn-based strategy game Searchuse. A lot of you guys suggested to add a tutorial, so I made one, and now it’s a lot easier to figure out how to play the game! I hope you enjoy....
|
1792846151.6578004
|
1792932551.6578004
|
|
26626
|
Grapefruit by @tsunami014 | Stardance
|
tsunami014
|
2
|
135.0
|
2180
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-12T22:08:50Z", "hours": 25.0, "multiplier": 16.69, "payout": 421, "blessing": "neutral", "text": "FIXED THE UI with dynamic colours and themes! Fixed some layouts Improved how tasks are made (including a not overwhelming amount of qualities and changed the ambiguous “reasoning” into a much less so “goals” As well as many minor also cool things! (See the devlogs after ship 1 for the full account)", "repo": "https://github.com/Tsunami014/Grapefruit/tree/v2.0", "demo": "https://github.com/Tsunami014/Grapefruit/releases/tag/v2.0"}, {"ship_num": 2, "ts": "2026-08-20T11:10:17Z", "hours": 110.0, "multiplier": 15.99, "payout": 1759, "blessing": "neutral", "text": "I finally decided I had done enough and it was really good! This app contains the conversation, a scene with Grapefruit in it, a basic settings page with not much in it, a tasks manager with categories (including a today category) and tasks with importance, reasoning, qualities, items! Also has a great bottom bar for easier text editing and has everything layed out in a mobile-friendly manner! Installation instructions are in the README", "repo": "https://github.com/Tsunami014/Grapefruit/tree/v2.0", "demo": "https://github.com/Tsunami014/Grapefruit/releases/tag/v2.0"}]...
|
[{"post_id": 66920, "ts": "202 [{"post_id": 66920, "ts": "2026-09-12T21:50:02Z", "hours": 2.17, "text": "I improved how tasks are made, making the task overlay centred instead of to the side, changed “reasoning” into “goals” which is easier to understand and still useful, reduced the number of qualities and added some new ones similar to the old reasoning (e.g. ‘obligation’ & ‘reward’), came up with MUCH better sample tasks and fixed a small issue with the bottom bar!"}, {"post_id": 65770, "ts": "2026-09-11T04:14:17Z", "hours": 6.3225, "text": "I did a bunch of work on the settings and added a “theme”! (Different variations of the base colour - which is now changeable in the settings!) I also used HSLuv instead of just HSL (which I tried before and thought it didn’t do much but now I realise it did help make everything nicer). I also added an effect so when you press on buttons it decreases the border radius a bit! And lastly I thought I’d fix some issues super quick due to the increased layout diversity from the settings but ended up spending much longer on them than expected, those being the jumpyness of the settings dropdowns (so I removed the animations and fixed it up a bit more and also fixed the flowlayout because it turns out everything is broken), fixed scrollbars inside other scrollbars, and probably more I forgot."}, {"post_id": 65060, "ts": "2026-09-09T22:07:39Z", "hours": 4.7747, "text": "Tweaked the colours lots and I’m now quite happy with how it’s at! Added auto generating colours for everything I didn’t before (the calendar, settings, importance and some buttons in overlays) Saved the dark/light theme to a settings file And fixed a bug with the confirm and move overlays being a strange fixed width!"}, {"post_id": 64549, "ts": "2026-09-09T01:39:01Z", "hours": 7.5414, "text": "Added dynamic colours! Don’t worry that it looks kinda bad right now, I had to learn UI design from scratch for this, and I will improve it soon. Made all the colours (well, most of them for now) generate based on a base colour and added a light and dark theme! I also made the Android navigation icons and the svgs change between light and dark properly. I spent quite some time trying to implement HSLuv or HCT in the hopes it would improve the colours but ended up scrapping that due to many reasons, including too much extra stuff, wasn’t making much (or any) difference, and my HCT implementation was just so broken and I wasn’t bothered to try and fix it. Also some parts are now extra broken, I’ll fix them soon too don’t worry! I had to devlog this in case I went over 10h by accident, hopefully will be resolved before the next one"}, {"post_id": 62540, "ts": "2026-09-05T23:38:38Z", "hours": 4.4194, "text": "Improved some UI layouts, clarified a sentence or two in the conversation, reordered the tasks page, made selecting the title in the task overlay hide the buttons (to make it easier to see), fixed some visual bugs (maybe?), improved some icons, changed ‘today’ to ‘starred’ (and made it not clear at the end of the day; I wanted to plan for the future) and last but not least I made the grey background for some of the overlays reach past the navigation AT LAST! So now the title bar doesn’t look off when the overlays are open!"}, {"post_id": 50885, "ts": "2026-08-20T10:54:28Z", "hours": 3.4444, "text": "Added an app icon and FINALLY got around to actually drawing the Grapefruit character! Also improved some parts of the conversation a bit more, improved the default tasks, improved the date importance calculations a smidge and fixed many tiny issues. It should now be DONE!"}, {"post_id": 47411, "ts": "2026-08-15T10:28:21Z", "hours": 0.8222, "text": "I thought I was done but turns out I forgot to add scrolling to the tasks list, so it would cause large issues if there were many tasks (or if the screen height was very low, as it does get on mobile when the keyboard is up)"}, {"post_id": 47355, "ts": "2026-08-15T07:43:42Z", "hours": 1.3553, "text": "Improved the “X weeks away” text (which was just wrong before) and added a today toggle and category button!"}, {"post_id": 46825, "ts": "2026-08-14T10:57:19Z", "hours": 6.6328, "text": "Did various small improvements, including: Many many bugfixes, rearranging the task and settings display to have buttons on the bottom (from user feedback), a help screen, a ‘move task to another category’ button and dialogue (which I cannot believe I forgot about until now) and added another purpose in the conversation for the user designated reason (so it actually gets shown sometimes)!"}, {"post_id": 42346, "ts": "2026-08-07T00:53:14Z", "hours": 5.9806, "text": "Added all sorts of cool things, including info in the settings page, bullet points to the text edits, changed the preset times and tasks, extra conversation features (failsafes and external functions), and improved the conversation some more (because it was fun)!"}, {"post_id": 41181, "ts": "2026-08-05T07:11:58Z", "hours": 8.2414, "text": "Finished the conversation! (It may change/get improved more later, but for now I’m happy with it!) I made many changes to some terrible phrasing choices I made previously (e.g. “But why won’t you do xyz? It’s super fun!” -> “You don’t have to do it if you don’t want to”), rewriting huge chunks of it and adding other huge chunks. (I hope you appreciate the dialogue, I’ve apparently spent 20 hours by now JUST on that, not even the code behind it) Also changed times to use +...m as well as +...h and included a preset range of times which are more on the short side to incentavise shorter tasks."}, {"post_id": 39244, "ts": "2026-08-02T02:43:56Z", "hours": 1.5244, "text": "Added buttons to insert new lines above/below, saving information to an external file automatically and trimming whitespace off of some things (a small convenience when on mobile)"}, {"post_id": 38611, "ts": "2026-08-01T02:54:26Z", "hours": 7.6272, "text": "Finalised the connection between the task engine and the conversation including adding variety, conversation suggestions knowing about the qualities and reasons, a significantly improved conversation (that’s almost done!!), a lot of conversation backend (including a custom parser for recursive synonym choices) and heaps more!"}, {"post_id": 36925, "ts": "2026-07-29T10:13:07Z", "hours": 6.4594, "text": "Spent pretty much the entire time working on the conversation choices and options (yes it’s that difficult). Also made some improvements to the backend (extra features making it easier/possible to do some things, fixed bugs). Still not done the conversation, will be another long stretch until I finish it but it’s nearly there!"}, {"post_id": 35517, "ts": "2026-07-27T00:18:06Z", "hours": 4.7739, "text": "Worked on the conversation, improving the quality of texts and options, and added some new options and features! Also added some cool mathy functions for determining task urgency/load!"}, {"post_id": 33452, "ts": "2026-07-23T12:45:03Z", "hours": 3.1211, "text": "Added the beginnings to the task importance system and implemented a cool task bottom text including progress, and also included that task progress in the task overlay popup at the bottom!"}, {"post_id": 33268, "ts": "2026-07-23T01:41:19Z", "hours": 1.11, "text": "Implemented a bunch of small improvements and fixes to the calendar popup and date highlight!"}, {"post_id": 32915, "ts": "2026-07-22T11:46:31Z", "hours": 4.1114, "text": "Spent the whole time working on a date selector! Also added text about the due date, e.g. “was due yesterday”, “due next Monday (in 5 days)”, etc."}, {"post_id": 32801, "ts": "2026-07-22T05:52:27Z", "hours": 1.6331, "text": "Implemented many extremely minor things brought up from user feedback, including: adding titles to the category and task list, using e.g. +2.5h instead of #E for counting time, improved the importance selection layout, ordering tasks, improving done task formatting (with tick and strikethrough) and other even more minor things!"}, {"post_id": 32262, "ts": "2026-07-21T10:09:04Z", "hours": 4.2289, "text": "Added a qualities screen to add qualities to tasks by clicking on the buttons and a lot of backend work to make everything run smoother and easier to code!"}, {"post_id": 31592, "ts": "2026-07-20T03:27:25Z", "hours": 3.4722, "text": "Added an importance slider, an info text and some minor things to the task overlay"}, {"post_id": 31054, "ts": "2026-07-19T04:15:30Z", "hours": 3.5669, "text": "Fixed a lot of bugs in the tasks area; including resizing issues, inability to scroll, and wrapping problems."}, {"post_id": 30597, "ts": "2026-07-18T09:33:18Z", "hours": 6.4275, "text": "Added task groups and an add, rename, and delete task group button that do things! Deleting, naming collisions and other problems are also handled with dialogues! Also added a slightly better colour scheme"}, {"post_id": 30005, "ts": "2026-07-17T07:38:26Z", "hours": 3.5383, "text": "Added a basic scene!!"}, {"post_id": 23344, "ts": "2026-07-06T07:27:06Z", "hours": 5.0297, "text": "Dramatically improved the backend for the conversation, adding many new features!"}, {"post_id": 23221, "ts": "2026-07-06T01:25:52Z", "hours": 5.4508, "text": "Added a parser for conversation backend data and an options list and text box to display it in!"}, {"post_id": 20674, "ts": "2026-07-02T10:21:27Z", "hours": 4.2228, "text": "Added a bottom bar to the task input including some utility functions like move up and down, a sample highlight that can get modified with the + and - timer buttons and a scrollbar!"}, {"post_id": 20454, "ts": "2026-07-02T01:36:55Z", "hours": 7.5497, "text": "Added an item input field with a bottom bar with utilities in it!"}, {"post_id": 19643, "ts": "2026-07-01T00:49:45Z", "hours": 4.1286, "text": "Added more to the UI layout, including a settings (with accordian sections) and new tasks (currently empty) page!"}, {"post_id": 19012, "ts": "2026-06-30T05:31:27Z", "hours": 5.6044, "text": "Added the very basic interface! The reason it took so long was because I had zero idea how to compile anything and took way too long figuring that out. But it can compile for desktop or mobile now! (although desktop is only for testing)"}]...
|
Title: Grapefruit by @tsunami014 | Stardance
Devl Title: Grapefruit by @tsunami014 | Stardance
Devlogs:
Added the very basic interface! The reason it took so long was because I had zero idea how to compile anything and took way too long figuring that out. But it can compile for desktop or mobile now! (although desktop is only for testing)
Added more to the UI layout, including a settings (with accordian sections) and new tasks (currently empty) page!
Added an item input field with a bottom bar with utilities in it!
Added a bottom bar to the task input including some utility functions like move up and down, a sample highlight that can get modified with the + and - timer buttons and a scrollbar!
Added a parser for conversation backend data and an options list and text box to display it in!
Dramatically improved the backend for the conversation, adding many new features!
Added a basic scene!!
Added task groups and an add, rename, and delete task group button that do things! Deleting, naming collisions and other problems are also handled with dialogues! Also added a slightly better colour scheme
Fixed a lot of bugs in the tasks area; including resizing issues, inability to scroll, and wrapping problems.
Added an importance slider, an info text and some minor things to the task overlay
Added a qualities screen to add qualities to tasks by clicking on the buttons and a lot of backend work to make everything run smoother and easier to code!
Implemented many extremely minor things brought up from user feedback, including: adding titles to the category and task list, using e.g. +2.5h instead of #E for counting time, improved the importance selection layout, ordering tasks, improving done task formatting (with tick and strikethrough) and other even more minor things!
Spent the whole time working on a date selector! Also added text about the due date, e.g. “was due yesterday”, “due next Monday (in 5 days)”, etc.
Implemented a bunch of small improvements and fixes to the calendar popup and date highlight!
Added the beginnings to the task importance system and implemented a cool task bottom text including progress, and also included that task progress in the task overlay popup at the bottom!
Worked on the conversation, improving the quality of texts and options, and added some new options and features! Also added some cool mathy functions for determining task urgency/load!
Spent pretty much the entire time working on the conversation choices and options (yes it’s that difficult). Also made some improvements to the backend (extra features making it easier/possible to do some things, fixed bugs). Still not done the conversation, will be another long stretch until I finish it but it’s nearly there!
Finalised the connection between the task engine and the conversation including adding variety, conversation suggestions knowing about the qualities and reasons, a significantly improved conversation (that’s almost done!!), a lot of conversation backend (including a custom parser for recursive synonym choices) and heaps more!
Added buttons to insert new lines above/below, saving information to an external file automatically and trimming whitespace off of some things (a small convenience when on mobile)
Finished the conversation! (It may change/get improved more later, but for now I’m happy with it!) I made many changes to some terrible phrasing choices I made previously (e.g. “But why won’t you do xyz? It’s super fun!” -> “You don’t have to do it if you don’t want to”), rewriting huge chunks of it and adding other huge chunks. (I hope you appreciate the dialogue, I’ve apparently spent 20 hours by now JUST on that, not even the code behind it) Also changed times to use +...m as well as +...h and included a preset range of times which are more on the short side to incentavise shorter tasks.
Added all sorts of cool things, including info in the settings page, bullet points to the text edits, changed the preset times and tasks, extra conversation features (failsafes and external functions), and improved the conversation some more (because it was fun)!
Did various small improvements, including: Many many bugfixes, rearranging the task and settings display to have buttons on the bottom (from user feedback), a help screen, a ‘move task to another category’ button and dialogue (which I cannot believe I forgot about until now) and added another purpose in the conversation for the user designated reason (so it actually gets shown sometimes)!
Improved the “X weeks away” text (which was just wrong before) and added a today toggle and category button!
I thought I was done but turns out I forgot to add scrolling to the tasks list, so it would cause large issues if there were many tasks (or if the screen height was very low, as it does get on mobile when the keyboard is up)
Added an app icon and FINALLY got around to actually drawing the Grapefruit character! Also improved some parts of the conversation a bit more, improved the default tasks, improved the date importance calculations a smidge and fixed many tiny issues. It should now be DONE!
Improved some UI layouts, clarified a sentence or two in the conversation, reordered the tasks page, made selecting the title in the task overlay hide the buttons (to make it easier to see), fixed some visual bugs (maybe?), improved some icons, changed ‘today’ to ‘starred’ (and made it not clear at the end of the day; I wanted to plan for the future) and last but not least I made the grey background for some of the overlays reach past the navigation AT LAST! So now the title bar doesn’t look off when the overlays are open!
Added dynamic colours! Don’t worry that it looks kinda bad right now, I had to learn UI design from scratch for this, and I will improve it soon. Made all the colours (well, most of them for now) generate based on a base colour and added a light and dark theme! I also made the Android navigation icons and the svgs change between light and dark properly. I spent quite some time trying to implement HSLuv or HCT in the hopes it would improve the colours but ended up scrapping that due to many reasons, including too much extra stuff, wasn’t making much (or any) difference, and my HCT implementation was just so broken and I wasn’t bothered to try and fix it. Also some parts are now extra broken, I’ll fix them soon too don’t worry! I had to devlog this in case I went over 10h by accident, hopefully will be resolved before the next one
Tweaked the colours lots and I’m now quite happy with how it’s at! Added auto generating colours for everything I didn’t before (the calendar, settings, importance and some buttons in overlays) Saved the dark/light theme to a settings file And fixed a bug with the confirm and move overlays being a strange fixed width!
I did a bunch of work on the settings and added a “theme”! (Different variations of the base colour - which is now changeable in the settings!) I also used HSLuv instead of just HSL (which I tried before and thought it didn’t do much but now I realise it did help make everything nicer). I also added an effect so when you press on buttons it decreases the border radius a bit! And lastly I thought I’d fix some issues super quick due to the increased layout diversity from the settings but ended up spending much longer on them than expected, those being the jumpyness of the settings dropdowns (so I removed the animations and fixed it up a bit more and also fixed the flowlayout because it turns out everything is broken), fixed scrollbars inside other scrollbars, and probably more I forgot.
I improved how tasks are made, making the task overlay centred instead of to the side, changed “reasoning” into “goals” which is easier to understand and still useful, reduced the number of qualities and added some new ones similar to the old reasoning (e.g. ‘obligation’ & ‘reward’), came up with MUCH better sample tasks and fixed a small issue with the bottom bar!
Ships:
I finally decided I had done enough and it was really good! This app contains the conversation, a scene with Grapefruit in it, a basic settings page with not much in it, a tasks manager with categories (including a today category) and tasks with importance, reasoning, qualities, items! Also has a great bottom bar for easier text editing and has everything layed out in a mobile-friendly manner! Installation instructions are in the README
FIXED THE UI with dynamic colours and themes! Fixed some layouts Improved how tasks are made (including a not overwhelming amount of qualities and changed the ambiguous “reasoning” into a much less so “goals” As well as many minor also cool things! (See the devlogs after ship 1 for the full account)...
|
1792849706.283899
|
1792936106.283899
|
|
5322
|
Kopuz by @temidaradev | Stardance
|
temidaradev
|
5
|
139.0
|
2306
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-14T16:31:49Z", "hours": 29.0, "multiplier": 19.98, "payout": 572, "blessing": "neutral", "text": "Two parts of the player stop lying to you: Recently Added lists what you actually added, and shuffle and repeat go dark when you switch them off. Apple Music takes any storefront, and Kopuz is now EUPL-1.2. Highlights Recently Added means recently added. The row was the alphabetical album list read backwards, so it showed the tail of your alphabet forever. Albums now rank by when their music arrived, stamped per file from the filesystem while the library is scanned, and the library’s own date added sort reads the same stamp. Rescan once after upgrading to fill it in. Shuffle and repeat clear their highlight. Dioxus merges an inline style instead of replacing it, so the empty “off” style could never unset the accent color and the button stayed lit for the rest of the session, with only the tooltip telling the truth. Both toggles carry their state in the class attribute now. Apple Music takes any storefront. Pick one or type the country code yourself, validated before it saves. Apple serves far more regions than the shortlist, and the storefront API is gated behind a developer account. EUPL-1.2. Kopuz was MIT, and is now licensed under the European Union Public Licence 1.2. Fixes Unsynced albums asked Subsonic for the wrong cover and fell back to an uncached request, so covers came back soft, and the three dots menu opened in the wrong place. Brave’s newer packaging ships the browser as brave-origin , which cookie import could not find. Windows with an app-drawn titlebar resize from any edge or corner on Linux. Flathub and Google Play badges on the README.", "repo": "https://github.com/Kopuz-org/kopuz", "demo": "https://github.com/Kopuz-org/kopuz/releases"}, {"ship_num": 2, "ts": "2026-08-24T14:51:45Z", "hours": 25.0, "multiplier": 17.59, "payout": 352, "blessing": "neutral", "text": "We released v0.16.0!!! Android is back and shippable, two new sources (Apple Music, Nextcloud), lyrics grew depth blur and instrumental breaks, and remote tracks start faster. The app identity finished its move to moe.kopuz.kopuz , so your data directory moves with it on macOS and Windows. Highlights Android builds again. arm64 APK on the releases page (API 24+), signed release job that fails a tagged build rather than shipping unsigned, F-Droid recipe, just android-release . Phone UI too: two line track rows, swipe gestures (up for fullscreen, left/right for next/prev), notification shuffle and repeat buttons, media permission flow, a keepalive waker so the player task keeps turning while hidden, WebView based YouTube deciphering, and an AMOLED theme (#634). Apple Music. Browser sign in, catalog and library browsing, favorites, playlists, downloads, word timed lyrics, per server storefront and language. Playback is hand rolled Widevine using a CDM borrowed from a browser you already have installed (never shipped with Kopuz); Android goes through MediaDrm . Streams decrypt progressively instead of pulling the whole file first (#455, closes #404). Nextcloud over WebDAV. App password auth. No music API there, so the library is read off the tree: folder is album, level above is artist. Listings carry no duration, so the scanner range requests the file head. Per server library folders with a PROPFIND picker and autodetect. Covers go embedded, then sidecar, then the preview endpoint. Use the Subsonic source if your instance runs the Music app (#629). Lyrics. Depth of field blur with a strength slider, synthesized instrumental breaks that fill left to right, the active line stays centered, changing songs no longer reads as a scroll, tab switches no longer desync, and a manual offset that auto fills from the backend’s reported output latency (#643, #647, #648). Remote tracks start faster. HTTP clients are pooled across tracks instead of one handshake per song, and the next 512 KiB window prefetches once reading passes the middle of the current one (#634). Radio uses the server’s analysis. OpenSubsonic sonicSimilarity (AudioMuse on Navidrome) when advertised, song seeded getSimilarSongs otherwise, seed at the head of the queue, and a 90s budget since cold metadata agent lookups were timing out silently (#647, 15615f36). Untrusted paths. Offline filenames are SHA-256 derived, extensions restricted, downloads staged before publication. Deletion only touches direct children of the offline cache or canonical files under a configured library root. Cancellation tombstones close the race where a worker finishing after a cancel re-registered the track (#650).", "repo": "https://github.com/Kopuz-org/kopuz", "demo": "https://github.com/Kopuz-org/kopuz/releases"}, {"ship_num": 3, "ts": "2026-08-04T16:26:49Z", "hours": 10.0, "multiplier": 19.86, "payout": 207, "blessing": "neutral", "text": "v0.14.0 is here!!! Now we are using automated builds with github actions! This one is mostly playlists and looks. Radio can start from a whole playlist now, the settings screen got rebuilt (prefer using top bar view in settings menu rather than cd if you are a linux user), and Kopuz can follow your wallpaper’s colors live. Releases finally ship proper packages for all three desktops. Highlights Radio from playlists: Sources can now seed a mix from an entire playlist instead of one track. YouTube Music does it first, and the button shows up on the playlist grid and the playlist page wherever a source supports it. YouTube liked music is also listed as a playlist of its own, so it gets the same treatment (#591, #592, #593). Matugen and pywal, live: Pick Matugen / Pywal (live) in Settings > Appearance and Kopuz recolors itself about half a second after the palette is regenerated. No restart. pywal needs nothing configured, matugen needs a small template. (#572). Settings rebuilt: The page is split up properly and there are now two layouts to choose from, CD (default) and top bar (#588). Helium for Spotify: Helium joins Chrome, Edge, Brave, Chromium, Vivaldi and Safari as a Web Playback SDK host. It needs Widevine pointed at manually; the README has the config (#574). Track numbers from tags: Album lists use the real metadata track number when there is one, instead of just counting rows (#581). Search in favorites: The favorites list filters as you type, with a proper no-results state (#587). Real release packages: One workflow now builds AppImage, DEB and RPM for Linux, a DMG for macOS, and EXE and MSI for Windows, with checksums. (probably no space will left for macos but eh) Fixes Selection bar was unreadable on light accents like Ayu Dark and Mirage (#599). Long album titles pushed the entire song list off screen (#580). Dots menus were getting clipped inside scrolling containers (#586). Fullscreen forgot the active tab when you hid and unhid the tabs panel (#585).", "repo": "https://github.com/Kopuz-org/kopuz", "demo": "https://github.com/Kopuz-org/kopuz/releases"}, {"ship_num": 4, "ts": "2026-07-24T21:24:05Z", "hours": 42.0, "multiplier": 19.65, "payout": 772, "blessing": "neutral", "text": "kopuz v0.12.0 is out. this one’s got some solid stuff. added isolated local music libraries so separate folders stay separate instead of all blending into one big pile. settings page got a full revision, it needed it. the cover art loading is a lot faster now since we defer cover indexing until after the metadata scan, so big libraries scan way quicker. also local artwork scales properly per view so no more blurry thumbnails. trickiest part was keeping the library scan from lagging everything, that’s sorted now. to test: point it at a couple different music folders and watch them stay isolated, covers should load smooth and settings should feel cleaner. (warning, parallelization: so uhh if you have a big lib, when you scroll in album page it will hurt your cpu a little because of the rendering but dont worry its a one time thing .d)", "repo": "https://github.com/Kopuz-org/kopuz", "demo": "https://github.com/Kopuz-org/kopuz/releases"}, {"ship_num": 5, "ts": "2026-06-27T18:33:59Z", "hours": 33.0, "multiplier": 15.36, "payout": 403, "blessing": "neutral", "text": "I made kopuz music player with the maintainers!! I submitted this project last event too! It got waaay better than first time also i will ship everytime we publish new version from now on! This release is mostly cleanup and refactor work: a big code split across server, utils, sources, and the playback controller, native Windows cookie decryption for sign-in, assets baked into the binary, and a bunch of new translations. Highlights Native Windows cookie decryption. YouTube Music and SoundCloud sign-in now read browser cookies natively on Windows instead of going through the rookie crate (which is now off on Windows). Sign-in works without the third-party cookie reader. Assets are baked into the binary. CSS, fonts, and the favicon now ship inside the kopuz binary, so it doesn’t need any external files at runtime. Big refactor. Server and utils are split up, the source model, page runtimes, backend modules, and playback controller are all broken out, and audio init, playback parsing, and config views got reworked. Dependencies are deduplicated through a virtual manifest to get ready for publishing on crates.io. Features yt-music showcase refactor. Focus indicators on interactive elements that didn’t have them. Matroska audio content-types now map to the mka decode hint. CSS, fonts, and favicon embedded into the binary. Dropped panicky .unwrap() calls, added crate docs, and moved eprintln! over to tracing . Deduplicated shared deps via a virtual manifest to prep for publish. Added contributing guidelines. New locales: Dutch, Malayalam, Tamil, Filipino, and European Portuguese (pt-PT), plus an updated Indonesian one. Fixes ytdlp : give yt-dlp a valid stdin so downloads work on Windows. queue : don’t split the queue when shuffling. server : gate the rookie browser-cookie import to non-Windows. db : tolerate line-ending-only migration checksum differences. lyrics : drop negative cache TTL from 7 days to 1 day. styles : stop the console warnings from re-diffing the stylesheet.", "repo": "https://github.com/Kopuz-org/kopuz", "demo": "https://github.com/Kopuz-org/kopuz/releases"}]...
|
[{"post_id": 68150, "ts": "202 [{"post_id": 68150, "ts": "2026-09-14T16:30:19Z", "hours": 9.9761, "text": "Eh i fixed some little bugs and reviewed prs, all these hours is just because i worked on daemon-frontend split i deployed the bugs fixed version for now, daemon is way harder than i expected thats why its taking sooooo long and too much effort. Fixed bugs are shuffle and repeat button werent updating their states but they now update .d"}, {"post_id": 60743, "ts": "2026-09-03T09:44:44Z", "hours": 10.7572, "text": "All right, so we got accepted to flathub and properly setted up our page, also now it releases on google play store properly too!!! I fixed some bugs and worked a lot for splitting daemon and frontend, hopefully it will be implemented soon. Lastly we switched to EUPL v1.2 license from MIT because i want to protect the daemon…"}, {"post_id": 54324, "ts": "2026-08-25T08:43:21Z", "hours": 8.6461, "text": "All right im in the middle of splitting the daemon from frontend, get ready for the holy kopuzd, we may release an official tui version too!!"}, {"post_id": 53815, "ts": "2026-08-24T14:49:03Z", "hours": 15.3031, "text": "I finally released android version again!! even its on google play store, also we did some improvements to the app. I started split daemon from the frontend, which will allow custom frontends soon, I dont have much photos to show and no long descrips because im too busy atm sorry .d we are mostly working on the backend currently."}, {"post_id": 49105, "ts": "2026-08-17T18:48:25Z", "hours": 10.1075, "text": "All right new log is here, so i was really busy with my job but i def worked on kopuz besides that. Firstly i added version and commit id to general settings, added progress stuff like in youtube progress bar to http streamed musics. Did a little bit of codebase cleanup, fixed a lot of RTL issues and now i splitted configs from db and now they are toml configable (hjem supported)."}, {"post_id": 40678, "ts": "2026-08-04T11:25:14Z", "hours": 3.0694, "text": "all right so i partially migrated CI/CD to self hosted ones because github actions SUCKS, also now Linux and Windows builds are made with github actions. Also added favorite, and 3 dots menu to fullscreen multimedia controller .d added search bar to favorites page, and i also made when listen from youtube music, discord presence will directly get the youtube music photo and doesnt fetch form musicbrainz, lastly some little bug fixes has made :3"}, {"post_id": 39386, "ts": "2026-08-02T10:23:53Z", "hours": 1.6589, "text": "I was so busy last 5 days for interview project but, good news i passed it! I wasnt able to develop kopuz at that time but its over, right after interview arc i redesigned the settings menu for better visibility and this is not the only option you can make it constant topbar but the default is cd selector so cope with it .d"}, {"post_id": 36306, "ts": "2026-07-28T10:21:27Z", "hours": 5.7181, "text": "so uhhh spotify integration is finally in master branch, people were just asking me when spotify when spotify and yeah its here, but for avoiding lawsuits i had to use public api which will give users some bad experience, but eh this can be used as “download your own music” propaganda, besides this i did some codebase cleanup and lil bug fixes"}, {"post_id": 34279, "ts": "2026-07-24T21:02:07Z", "hours": 7.8978, "text": "all right, so i was like super bored and randomly started add stuff, so uhh we now have isolated local music libraries, so like if you have 2 different folders which contains different kinds of music and you dont want to mix it with your main library, i had that issue so added this option, also people were complaining about settings menu so i made exactly how they wanted, also when i woke up this morning i saw a discussion opened in the repo which said, “kopuz individually takes images of flac files” which is true, i didnt expect anyone with 5k songs to use this app .d Anyways i fixed that issue and the app got faster (i was so stupid while i was enabling individual image scanning)."}, {"post_id": 32862, "ts": "2026-07-22T09:32:47Z", "hours": 8.7542, "text": "spotify support is postponed to v0.12.0, but uhh we got some solid stuff for v0.11.0 fullscreen got reworked, now with cover art backgrounds and a general cleanup pass added quick search so you can actually find your stuff fast custom UI font support, use whatever font you want redesigned the normal radio page radio-browser.info search, ICY now-playing metadata, and playlist resolution EQ now has 10 bands instead of the old setup auto-scroll pauses when you scroll manually, plus a sync button added Helium browser for browser sign-in app now checks if the media server is actually reachable instead of assuming it is added Swedish locale"}, {"post_id": 30196, "ts": "2026-07-17T16:06:51Z", "hours": 5.7239, "text": "I added spotify support with Public API, since this is a big app, im afraid of using the illegal way, so for using the spotify integration there MUST be a browser which has widevine capability… Is it annoying? yes. But is it working? yes."}, {"post_id": 28454, "ts": "2026-07-14T18:23:33Z", "hours": 8.9486, "text": "Okay so, firstly i added list view to album and artist menu, its kinda a cheat for better performance since the images are getting smaller, users always complain about bad performance on higher resolutions so its a step against it, also i added highly customazible sorting options because it was a pr back then and the author of that pr couldnt reach to his pc so i took over his task, also not MPRIS and darwin now playing integration supports seeking, lastly i started on TIDAL integration because vaxry requested it .d"}, {"post_id": 23726, "ts": "2026-07-06T20:51:28Z", "hours": 2.9472, "text": "Revised settings page for better understandability for users. Yeah so vaxry told me that settings menu sucks. So i made the components like this in the photo, easily understandable (idiotproofing)"}, {"post_id": 22262, "ts": "2026-07-04T17:15:09Z", "hours": 3.3267, "text": "I reinforced ListenBrainz scrobbling because vaxry reported that it sucks, so apperantly it was half my issue and half LB’s issue because BTS listeners migrated to libre.fm and listenbrainz from last.fm. So scrobbling was a bit wrong in my side, so fixed that. Also im waiting for a contributor to add offline scrobbling ability which will fully fix this issue since it will cache listening and when the LB server is awake again it will dump scrobbles… Ah also now playing was a bit broken so i fully fixed it too, now the app has great logs for scrobble stuff…"}, {"post_id": 20754, "ts": "2026-07-02T13:28:30Z", "hours": 4.5308, "text": "Added enhanced discord presence which now has the option to show the source of your music, also i added clear image cache and rescan buttons for easier image handling."}, {"post_id": 17070, "ts": "2026-06-27T18:31:27Z", "hours": 1.8081, "text": "fixed source selector part styling and with contributors we fixed login issue on windows, established ai policy, and a lot more!!"}, {"post_id": 15158, "ts": "2026-06-24T16:58:45Z", "hours": 4.4406, "text": "worked on adding librespot backend (kinda), since spotify isnt giving us any music or music data bytes we cant decode anything, so i made librespot to fetch the stuff in logged in profile and play via anon yt music backend with metadata matching, its still a pr but eh hopefully it will be good"}, {"post_id": 13216, "ts": "2026-06-21T17:24:11Z", "hours": 2.4389, "text": "better album view in yt music, fixed search for yt music, discover menu is now better, any album available can now viewable. Also we have migrated to sqlite from json so the app became waaay faster and now there is no seperation between local and server now everything is in one place."}, {"post_id": 12648, "ts": "2026-06-20T17:42:21Z", "hours": 0.8142, "text": "made last touches with the contributor and made it ready to release! hopefully first release in stardance will be v0.7.0, who knows kopuz may become stable in stardance…"}, {"post_id": 12101, "ts": "2026-06-19T19:09:14Z", "hours": 1.3692, "text": "fixed fullscreen view, it was really poor and multimedia controls were so bad, i took the audio control to top right corner so it looks like apple music now."}, {"post_id": 11511, "ts": "2026-06-18T20:02:54Z", "hours": 3.7036, "text": "i added mini player and tray implementation for users!!"}, {"post_id": 9168, "ts": "2026-06-15T11:15:59Z", "hours": 10.3256, "text": "I worked on adding spotify but failed, same for apple music too but at least i added soundcloud to kopuz!!! This music player will be the best music player ever created for every platform user!"}, {"post_id": 5936, "ts": "2026-06-09T22:34:46Z", "hours": 1.9461, "text": "local metadata viewer and editor added"}, {"post_id": 4334, "ts": "2026-06-07T16:45:21Z", "hours": 4.0619, "text": "we got youtube music support now!! with a lot of bug fixes"}, {"post_id": 942, "ts": "2026-06-02T18:20:14Z", "hours": 1.8019, "text": "Making kopuz better this time huh?"}]...
|
Title: Kopuz by @temidaradev | Stardance
Devlogs: Title: Kopuz by @temidaradev | Stardance
Devlogs:
Making kopuz better this time huh?
we got youtube music support now!! with a lot of bug fixes
local metadata viewer and editor added
I worked on adding spotify but failed, same for apple music too but at least i added soundcloud to kopuz!!! This music player will be the best music player ever created for every platform user!
i added mini player and tray implementation for users!!
fixed fullscreen view, it was really poor and multimedia controls were so bad, i took the audio control to top right corner so it looks like apple music now.
made last touches with the contributor and made it ready to release! hopefully first release in stardance will be v0.7.0, who knows kopuz may become stable in stardance…
better album view in yt music, fixed search for yt music, discover menu is now better, any album available can now viewable. Also we have migrated to sqlite from json so the app became waaay faster and now there is no seperation between local and server now everything is in one place.
worked on adding librespot backend (kinda), since spotify isnt giving us any music or music data bytes we cant decode anything, so i made librespot to fetch the stuff in logged in profile and play via anon yt music backend with metadata matching, its still a pr but eh hopefully it will be good
fixed source selector part styling and with contributors we fixed login issue on windows, established ai policy, and a lot more!!
Added enhanced discord presence which now has the option to show the source of your music, also i added clear image cache and rescan buttons for easier image handling.
I reinforced ListenBrainz scrobbling because vaxry reported that it sucks, so apperantly it was half my issue and half LB’s issue because BTS listeners migrated to libre.fm and listenbrainz from last.fm. So scrobbling was a bit wrong in my side, so fixed that. Also im waiting for a contributor to add offline scrobbling ability which will fully fix this issue since it will cache listening and when the LB server is awake again it will dump scrobbles… Ah also now playing was a bit broken so i fully fixed it too, now the app has great logs for scrobble stuff…
Revised settings page for better understandability for users. Yeah so vaxry told me that settings menu sucks. So i made the components like this in the photo, easily understandable (idiotproofing)
Okay so, firstly i added list view to album and artist menu, its kinda a cheat for better performance since the images are getting smaller, users always complain about bad performance on higher resolutions so its a step against it, also i added highly customazible sorting options because it was a pr back then and the author of that pr couldnt reach to his pc so i took over his task, also not MPRIS and darwin now playing integration supports seeking, lastly i started on TIDAL integration because vaxry requested it .d
I added spotify support with Public API, since this is a big app, im afraid of using the illegal way, so for using the spotify integration there MUST be a browser which has widevine capability… Is it annoying? yes. But is it working? yes.
spotify support is postponed to v0.12.0, but uhh we got some solid stuff for v0.11.0 fullscreen got reworked, now with cover art backgrounds and a general cleanup pass added quick search so you can actually find your stuff fast custom UI font support, use whatever font you want redesigned the normal radio page radio-browser.info search, ICY now-playing metadata, and playlist resolution EQ now has 10 bands instead of the old setup auto-scroll pauses when you scroll manually, plus a sync button added Helium browser for browser sign-in app now checks if the media server is actually reachable instead of assuming it is added Swedish locale
all right, so i was like super bored and randomly started add stuff, so uhh we now have isolated local music libraries, so like if you have 2 different folders which contains different kinds of music and you dont want to mix it with your main library, i had that issue so added this option, also people were complaining about settings menu so i made exactly how they wanted, also when i woke up this morning i saw a discussion opened in the repo which said, “kopuz individually takes images of flac files” which is true, i didnt expect anyone with 5k songs to use this app .d Anyways i fixed that issue and the app got faster (i was so stupid while i was enabling individual image scanning).
so uhhh spotify integration is finally in master branch, people were just asking me when spotify when spotify and yeah its here, but for avoiding lawsuits i had to use public api which will give users some bad experience, but eh this can be used as “download your own music” propaganda, besides this i did some codebase cleanup and lil bug fixes
I was so busy last 5 days for interview project but, good news i passed it! I wasnt able to develop kopuz at that time but its over, right after interview arc i redesigned the settings menu for better visibility and this is not the only option you can make it constant topbar but the default is cd selector so cope with it .d
all right so i partially migrated CI/CD to self hosted ones because github actions SUCKS, also now Linux and Windows builds are made with github actions. Also added favorite, and 3 dots menu to fullscreen multimedia controller .d added search bar to favorites page, and i also made when listen from youtube music, discord presence will directly get the youtube music photo and doesnt fetch form musicbrainz, lastly some little bug fixes has made :3
All right new log is here, so i was really busy with my job but i def worked on kopuz besides that. Firstly i added version and commit id to general settings, added progress stuff like in youtube progress bar to http streamed musics. Did a little bit of codebase cleanup, fixed a lot of RTL issues and now i splitted configs from db and now they are toml configable (hjem supported).
I finally released android version again!! even its on google play store, also we did some improvements to the app. I started split daemon from the frontend, which will allow custom frontends soon, I dont have much photos to show and no long descrips because im too busy atm sorry .d we are mostly working on the backend currently.
All right im in the middle of splitting the daemon from frontend, get ready for the holy kopuzd, we may release an official tui version too!!
All right, so we got accepted to flathub and properly setted up our page, also now it releases on google play store properly too!!! I fixed some bugs and worked a lot for splitting daemon and frontend, hopefully it will be implemented soon. Lastly we switched to EUPL v1.2 license from MIT because i want to protect the daemon…
Eh i fixed some little bugs and reviewed prs, all these hours is just because i worked on daemon-frontend split i deployed the bugs fixed version for now, daemon is way harder than i expected thats why its taking sooooo long and too much effort. Fixed bugs are shuffle and repeat button werent updating their states but they now update .d
Ships:
I made kopuz music player with the maintainers!! I submitted this project last event too! It got waaay better than first time also i will ship everytime we publish new version from now on! This release is mostly cleanup and refactor work: a big code split across server, utils, sources, and the playback controller, native Windows cookie decryption for sign-in, assets baked into the binary, and a bunch of new translations. Highlights Native Windows cookie decryption. YouTube Music and SoundCloud sign-in now read browser cookies natively on Windows instead of going through the rookie crate (which is now off on Windows). Sign-in works without the third-party cookie reader. Assets are baked into the binary. CSS, fonts, and the favicon now ship inside the kopuz binary, so it doesn’t need any external files at runtime. Big refactor. Server and utils are split up, the source model, page runtimes, backend modules, and playback controller are all broken out, and audio init, playback parsing, and config views got reworked. Dependencies are deduplicated through a virtual manifest to get ready for publishing on crates.io. Features yt-music showcase refactor. Focus indicators on interactive elements that didn’t have them. Matroska audio content-types now map to the mka decode hint. CSS, fonts, and favicon embedded into the binary. Dropped panicky .unwrap() calls, added crate docs, and moved eprintln! over to tracing . Deduplicated shared deps via a virtual manifest to prep for publish. Added contributing guidelines. New locales: Dutch, Malayalam, Tamil, Filipino, and European Portuguese (pt-PT), plus an updated Indonesian one. Fixes ytdlp : give yt-dlp a valid stdin so downloads work on Windows. queue : don’t split the queue when shuffling. server : gate the rookie browser-cookie import to non-Windows. db : tolerate line-ending-only migration checksum differences. lyrics : drop negative cache TTL from 7 days to 1 day. styles : stop the console warnings from re-diffing the stylesheet.
kopuz v0.12.0 is out. this one’s got some solid stuff. added isolated local music libraries so separate folders stay separate instead of all blending into one big pile. settings page got a full revision, it needed it. the cover art loading is a lot faster now since we defer cover indexing until after the metadata scan, so big libraries scan way quicker. also local artwork scales properly per view so no more blurry thumbnails. trickiest part was keeping the library scan from lagging everything, that’s sorted now. to test: point it at a couple different music folders and watch them stay isolated, covers should load smooth and settings should feel cleaner. (warning, parallelization: so uhh if you have a big lib, when you scroll in album page it will hurt your cpu a little because of the rendering but dont worry its a one time thing .d)
v0.14.0 is here!!! Now we are using automated builds with github actions! This one is mostly playlists and looks. Radio can start from a whole playlist now, the settings screen got rebuilt (prefer using top bar view in settings menu rather than cd if you are a linux user), and Kopuz can follow your wallpaper’s colors live. Releases finally ship proper packages for all three desktops. Highlights Radio from playlists: Sources can now seed a mix from an entire playlist instead of one track. YouTube Music does it first, and the button shows up on the playlist grid and the playlist page wherever a source supports it. YouTube liked music is also listed as a playlist of its own, so it gets the same treatment (#591, #592, #593). Matugen and pywal, live: Pick Matugen / Pywal (live) in Settings > Appearance and Kopuz recolors itself about half a second after the palette is regenerated. No restart. pywal needs nothing configured, matugen needs a small template. (#572). Settings rebuilt: The page is split up properly and there are now two layouts to choose from, CD (default) and top bar (#588). Helium for Spotify: Helium joins Chrome, Edge, Brave, Chromium, Vivaldi and Safari as a Web Playback SDK host. It needs Widevine pointed at manually; the README has the config (#574). Track numbers from tags: Album lists use the real metadata track number when there is one, instead of just counting rows (#581). Search in favorites: The favorites list filters as you type, with a proper no-results state (#587). Real release packages: One workflow now builds AppImage, DEB and RPM for Linux, a DMG for macOS, and EXE and MSI for Windows, with checksums. (probably no space will left for macos but eh) Fixes Selection bar was unreadable on light accents like Ayu Dark and Mirage (#599). Long album titles pushed the entire song list off screen (#580). Dots menus were getting clipped inside scrolling containers (#586). Fullscreen forgot the active tab when you hid and unhid the tabs panel (#585).
We released v0.16.0!!! Android is back and shippable, two new sources (Apple Music, Nextcloud), lyrics grew depth blur and instrumental breaks, and remote tracks start faster. The app identity finished its move to moe.kopuz.kopuz , so your data directory moves with it on macOS and Windows. Highlights Android builds again. arm64 APK on the releases page (API 24+), signed release job that fails a tagged build rather than shipping unsigned, F-Droid recipe, just android-release . Phone UI too: two line track rows, swipe gestures (up for fullscreen, left/right for next/prev), notification shuffle and repeat buttons, media permission flow, a keepalive waker so the player task keeps turning while hidden, WebView based YouTube deciphering, and an AMOLED theme (#634). Apple Music. Browser sign in, catalog and library browsing, favorites, playlists, downloads, word timed lyrics, per server storefront and language. Playback is hand rolled Widevine using a CDM borrowed from a browser you already have installed (never shipped with Kopuz); Android goes through MediaDrm . Streams decrypt progressively instead of pulling the whole file first (#455, closes #404). Nextcloud over WebDAV. App password auth. No music API there, so the library is read off the tree: folder is album, level above is artist. Listings carry no duration, so the scanner range requests the file head. Per server library folders with a PROPFIND picker and autodetect. Covers go embedded, then sidecar, then the preview endpoint. Use the Subsonic source if your instance runs the Music app (#629). Lyrics. Depth of field blur with a strength slider, synthesized instrumental breaks that fill left to right, the active line stays centered, changing songs no longer reads as a scroll, tab switches no longer desync, and a manual offset that auto fills from the backend’s reported output latency (#643, #647, #648). Remote tracks start faster. HTTP clients are pooled across tracks instead of one handshake per song, and the next 512 KiB window prefetches once reading passes the middle of the current one (#634). Radio uses the server’s analysis. OpenSubsonic sonicSimilarity (AudioMuse on Navidrome) when advertised, song seeded getSimilarSongs otherwise, seed at the head of the queue, and a 90s budget since cold metadata agent lookups were timing out silently (#647, 15615f36). Untrusted paths. Offline filenames are SHA-256 derived, extensions restricted, downloads staged before publication. Deletion only touches direct children of the offline cache or canonical files under a configured library root. Cancellation tombstones close the race where a worker finishing after a cancel re-registered the track (#650).
Two parts of the player stop lying to you: Recently Added lists what you actually added, and shuffle and repeat go dark when you switch them off. Apple Music takes any storefront, and Kopuz is now EUPL-1.2. Highlights Recently Added means recently added. The row was the alphabetical album list read backwards, so it showed the tail of your alphabet forever. Albums now rank by when their music arrived, stamped per file from the filesystem while the library is scanned, and the library’s own date added sort reads the same stamp. Rescan once after upgrading to fill it in. Shuffle and repeat clear their highlight. Dioxus merges an inline style instead of replacing it, so the empty “off” style could never unset the accent color and the button stayed lit for the rest of the session, with only the tooltip telling the truth. Both toggles carry their state in the class attribute now. Apple Music takes any storefront. Pick one or type the country code yourself, validated before it saves. Apple serves far more regions than the shortlist, and the storefront API is gated behind a developer account. EUPL-1.2. Kopuz was MIT, and is now licensed under the European Union Public Licence 1.2. Fixes Unsynced albums asked Subsonic for the wrong cover and fell back to an uncached request, so covers came back soft, and the three dots menu opened in the wrong place. Brave’s newer packaging ships the browser as brave-origin , which cookie import could not find. Windows with an app-drawn titlebar resize from any edge or corner on Linux. Flathub and Google Play badges on the README....
|
1792842622.5745435
|
1792929022.5745435
|
|
34953
|
Ultimate Mob Farms by @OffsetMonkey538 | Stardance
|
OffsetMonkey538
|
1
|
139.24
|
2678
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-19T13:59:43Z", "hours": 125.0, "multiplier": 18.99, "payout": 2678, "blessing": "neutral", "text": "This is a Minecraft mod for making mob farms, something similar to for example Mob Grinding Utils. It features item and xp collection, entity movement, spawner relocation and crafting, single block mob farms and some other stuff. Honestly, I’m quite proud just over the fact that I finally did finish this project (or at least got it to the point of being comfortable releasing it publically) and it’s hard to choose any specific features, but here I go… I do really like how the particle effects for the soul fan turned out, they’re quite satisfying imo. This is kind of unrelated to the mod, but I’m also proud of having helped out with the development of RRV, the recipe viewer mod I added compat with. While adding support for it in this mod, I noticed some bugs and crashes and stuff, which I PRed fixes for. It’s a bigger project with many complex things, so coming up with a list of all the things I found hard or took a lot of time and explaining all of them would probably go over whatever character limit this text box has lol. One thing that was a big PITA was the compact mob farm inventory. The original implementation I wrote was just… completely wrong, so I had to rewrite it. Then later I realized I overlooked like the most important part of the item transfer api (transactions, which are used for rolling back changes and stuff) so I had to rewrite it again, and then after that I rewrote quite a bit of it one more time because, after the last rewrite, I had accidentally made it possible for other blocks to insert into the inventory, which isn’t something I wanted as that seems quite OP. Another thing that comes to mind is figuring out the code for the soul fan’s range based on upgrades and strength at a given distance. The problem was that the coordinate you have to increase/decrease depends on the orientation of the block, so as an example, the depth upgrade would need to increase the Y pos when the block is placed pointing up and decrease it when pointing down, the X pos when facing north/south and the Z pos when facing east/west. For pushing the entities, I had to find the coordinates of the middle of the face the block is pointing, and add/subtract the correct coordinate to get the range in the depth direction, and then find the distance between each entity currently in range and the fan in just that plane for calculating the strength. For people testing out the project, I definitely do recommend also installing the Reliable Recipe Viewer (RRV) and Item Description mods", "repo": "https://git.offsetmonkey538.top/OffsetMods538/Ultimate-Mob-Farms", "demo": "https://modrinth.com/mod/ultimate-mob-farms"}]...
|
[{"post_id": 80728, "ts": "202 [{"post_id": 80728, "ts": "2026-10-18T17:03:06Z", "hours": 1.5967, "text": "Started working on setting up a docs page for the mod. Mainly want it for explaining the data driven configuration, but having full pages to give more detailed explanations of the different features is also probably a nice touch. Made a new icon for the mod as well, it’s pretty much the same but higher res because the 256x256 one looked weird on the docs page. The docs are done using Starlight like all my other docs pages (see those here ) and lives at https://ultimate-mob-farms.docs.cursedmods.com/ and on the docs/master branch in the repo Currently it’s just the landing page, but it’ll have more stuff soon enough"}, {"post_id": 79375, "ts": "2026-10-13T18:29:39Z", "hours": 3.9611, "text": "Added an RRV screen showing what items can be acquired from the Compact Mob Farm. Shows the entity, the items it drops and the required tier upgrade level. Hoping this also helps with confusion on how stuff works"}, {"post_id": 76204, "ts": "2026-10-02T17:34:45Z", "hours": 2.1567, "text": "Added a progress bar to the Compact Mob Farm GUI that hopefully makes it much easier to understand when it’ll damage the entity and it doesn’t look like it’s just doing nothing lol, especially with the 30 second default time"}, {"post_id": 75664, "ts": "2026-09-30T17:54:02Z", "hours": 6.5264, "text": "Waow that’s quite a bit of stardust 😅 Quite happy with this project and the ratings it got on the first ship :D (except the one that accused my devlogs of being written by ai… My writing is not that bad, right? ) I noticed in the shipwrights’ review video that how upgrades work is not very obvious, they were trying to put a tier upgrade into a soul fan, which only accepts width, height and depth upgrades. It was mentioned in ratings that stuff is a bit hard to figure out in the beginning as well. So to fix that, I added some silhouettes of the supported upgrades shown in the slots and also a hover tooltip saying what’s supported. Oh and i’ve also added 26.3 support now"}, {"post_id": 70575, "ts": "2026-09-19T11:52:49Z", "hours": 7.4719, "text": "School starting with september really hit my productivity on this mod like a truck I am realizing the deadline for stardance is getting real close, so I think this is probably the point where I’ll have to ship it. There are still some TODOs in the code and bugs that need to be squashed and porting to 26.3 (which released like this week, thanks mojang ) and so I haven’t been comfortable marking the latest release as 1.0.0 yet or making announcements elsewhere, but the mod is now perfectly usable and I like where it is. Changes since last devblog: 26.2 support Injecting some items into vanilla loot chests Advancements 26.2 wasn’t actually as painful as I thought it’d be, which was a nice surprise. Adding advancements was a bit more of a pain with 26.2 support though as I needed custom criteria for some of the advancements (one for putting a soul gem in the compact mob farm and another for using a soul gem on a spawner) and Mojang, in their infinite wisdom, decided to move a bunch of advancement stuff around in 26.2."}, {"post_id": 62252, "ts": "2026-09-05T15:04:19Z", "hours": 6.3956, "text": "Getting closer and closer to the actual release… could this be the last devblog before that? Still not quite 100% about if the required kills for each mob are balanced, but it’s probably good enough. Definitely better than everything being 10 kills tho. Felt very off-balance when withers, which are hard to spawn and hard to kill, and pigs, which are just everywhere, required the same amount of kills to capture. Notable changes: Show soul capturing recipe in rrv when checking usages of spawn eggs Show tooltip with entity name when hovering entity in soul capturing rrv recipe Add item descriptions Fix being able to insert items into the compact mob farm inventory Make amount of kills required for capturing an entity different per entity. Update compact mob farm recipe (now uses soul spikes in the middle instead of stone bricks) Store soul gem colors as hex strings in entity tiers config. Much more understandable and easier to manually change the overrides config or whatever Make built-in packs use the mod icon using a mixin instead of copying the icon into the packs. This shaves like 80kb off the final jar which is quite a bit of unnecessary duplicate crap Remove some log spam that was for debugging"}, {"post_id": 59802, "ts": "2026-09-01T19:04:17Z", "hours": 2.5989, "text": "checking the recipe for a specific soul gem now only shows that specific entity instead of all soul capturing recipes soul capturing recipe now shows up before the duplication recipe soul gems can now be searched by the name of the entity they contain fix compact mob farm gui not rendering vanilla background overlay allow filling soul gem when held in the main hand fix dedicated server crashing Running in a dedicated server would crash because there are quite a few places where I referenced client-only classes in places that the jvm will load on the dedicated server as well. Mostly in the different block entity classes, where I had a clientTick method that had ClientLevel as one of it’s parameters. Even though that method would never get called on the dedicated server, java still wants it to exist when loading the class because it’s part of a method’s definition. My current solution is very very awful… I moved those things into methods in the client initializer and… well… yeah… it just looks and is awful: https://git.offsetmonkey538.top/OffsetMods538/Ultimate-Mob-Farms/src/commit/0.0.7/modded/src/main/java/top/offsetmonkey538/ultimatemobfarms/modded/UltimateMobFarmsModdedClient.java#L65 The real, foolproof solution would be using the split sources feature of loom, which creates a second source set and makes the main source set unable to access client classes. Only problem is that it’d be a bit annoying to fully get set up in my multiloader-version gradle system and would most definitely need many changes on MonkeyLib538 as well and I just am not up for doing that atm."}, {"post_id": 58874, "ts": "2026-08-31T13:27:24Z", "hours": 8.6083, "text": "Fixed compact mob farm inventory not working correctly and being incompatible with other mods’ item movement stuff. The whole point of the transfer API on both fabric and neoforge is the transaction system, where each change must store a snapshot that can be returned to if needed; Turns out ignoring the TransactionContext and not saving any snapshots is a bad idea 😅 I’ve tested and it’s compatible with create fly (see screenshot where a compact mob farm with a blaze is being used to feed a blaze burner for a steam engine) and most probably other mods as well. Added Reliable Recipe Viewer integration that explains soul capturing"}, {"post_id": 54946, "ts": "2026-08-26T11:20:57Z", "hours": 7.2406, "text": "Bunch of refactoring, bug fixes, balancing, etc. Release notes can be seen on the forgejo release or on modrinth, I’m not gonna copy paste em here (this is version 0.0.5 right now) It is now actually reviewed on Modrinth, took two weeks for that to happen (Gosh I remember when review times were like less than 24 hours ). I’m still keeping it unlisted until I get a 1.0.0 release out, but if anyone wants to check it out and share feedback or anything, then it’s right over here: https://modrinth.com/mod/ultimate-mob-farms Before a v1, I still need better rrv and item descriptions integration, some more balancing changes, advancements, quite a few bugfixes and probably some other stuff I’m forgetting at the moment."}, {"post_id": 50153, "ts": "2026-08-19T08:08:41Z", "hours": 2.3531, "text": "Made soul fans placed at different locations use a different starting frame offset, so they don’t all look the same (see before and after videos). The way this is implemented is a bit interesting… There’s no easy “use this model but offset any texture’s animation by 1 frame” as afaik the frame index is global per texture. What Minecraft does allow is each block state variant actually being an array of variants that the game will choose between using afaik a hash derived from the block position. This is used in vanilla for things like stone and deepslate, which have mirrored and rotated variants so caves look less repetitive or whatever. As each texture has a global frame index, I am duplicating the soul fan texture and just changing the order of the frames in the accompanying animation metadata file and creating separate models which point to the different textures and then those models are used as variants in the blockstate. What’s even more fun is I am using datagen for duplicating the texture and creating the animation metadata files, which definitely isn’t concerning at all. This also required me to make a pull request to fabric api because the FabricCodecDataProvider assumes all files it generates are .json files, but animation metadata is .mcmeta. (The PR isn’t yet merged so I resorted to…. injecting a mixin into fabric api, definitely not cursed at all I swear)"}, {"post_id": 49673, "ts": "2026-08-18T15:46:30Z", "hours": 4.43, "text": "Added particle effects for soul fan. Figuring out where to spawn em was quite fun actually, though I did have to add a custom particle (reuses the vanilla “small gust” texture) because turns out most particles won’t obey the velocity you give them and just do their own thing, which was a bit annoying."}, {"post_id": 49138, "ts": "2026-08-17T19:28:11Z", "hours": 3.7422, "text": "Added range upgrades for the fan and collector blocks. It was a bit challenging figuring out actually extending the bounding box in the correct directions based on the block is facing, but I did get it working"}, {"post_id": 48767, "ts": "2026-08-17T09:17:16Z", "hours": 4.6397, "text": "Implemented functionality of the soul fan, getting the strength to scale based on distance was a bit of a pain cause i needed to get the position of the center of the face that the fan’s pointing in and only calculate the distance to the entity based on the correct axis cause otherwise giving it a larger range would make the farther to the side entities get pushed less and uhh yeah, took quite a bit of experimenting with stuff to get it to work correctly"}, {"post_id": 47706, "ts": "2026-08-15T19:55:20Z", "hours": 0.9575, "text": "Spent the last hour and a half faffing about with my loader abstraction in monkeylib (stardance only recorded an hour cause that’s technically a separate project) for creative tabs. All that just cause I really wanted the RRV stack group for the filled soul gems to show up next to the empty soul gem, instead of at the end of the list where it did previously"}, {"post_id": 47530, "ts": "2026-08-15T14:35:38Z", "hours": 3.0928, "text": "Spent a bunch of time on cleaning up some code and abstracting some things out of collector block classes to make it easier to add functionality of the soul fan later on, speaking of which! the soul fan is a block that exists in game now"}, {"post_id": 46880, "ts": "2026-08-14T12:46:12Z", "hours": 0.2881, "text": "Spent some time thinking about what I’m gonna do for the block that moves mobs around. I think I’ve decided on going with a basic “fan” type block and now started working on what it could look like. I’m imagining the souls kinda like escaping from the soul sand and also particles spawning and moving in the direction it’s pushing. As always I’m pretty bad at drawing textures, so the beginning here looks a bit crude, but it’s definitely a start. It’d be really nice if aseprite had a wakatime plugin…"}, {"post_id": 46845, "ts": "2026-08-14T11:27:58Z", "hours": 3.8683, "text": "Added the experience collector block, which obviously picks up xp orbs and turns em into bottles of enchanting. Kind of got so focused I forgot about making more detailed devlogs 😅"}, {"post_id": 46238, "ts": "2026-08-13T10:39:10Z", "hours": 3.695, "text": "Made the soul spikes actually deal extra fall damage when fell on. Current fall damage multiplier is at 5, not sure if that is too much or too little. The damage is done by a FakePlayer, so the mob will also drop loot that only drops from player kills, including experience orbs. (Seems like I need to add an XP Collector block in the future to deal with those). Oh and also did some boilerplate-ish things to soul spikes like adding a recipe, translation, etc Turns out minecraft decides that the block you fall on is the one that’s 0.2 blocks below you…. The model of the soul spikes (which i don’t really wanna change anymore) has the base at 3 pixels high, which makes it… 0.1875 block high 😮💨 Nothing that can’t be solved with a mixin to Entity#getOnPosLegacy that changes the BlockPos it returns if the the block above the “correct” block is soul spikes"}, {"post_id": 45189, "ts": "2026-08-11T14:44:28Z", "hours": 0.2731, "text": "Made an okay-ish texture for the soul spikes block (many of em at once looks kinda noisy but oh well, it’ll do. Hopefully it’ll be locked up in a farm killing chamber room anyway). Had to change the UV mapping again because for whatever reasons, minecraft decides to render a sliver of the neighboring pixel as well which looks super bad with two very contrasty colors next to each other. Solution was to move the blue parts to the bottom of the texture where there was room to have it separated by a row of blank pixels."}, {"post_id": 45107, "ts": "2026-08-11T11:51:36Z", "hours": 0.7186, "text": "Finished UV mapping the spikes…. this literally took like over an hour haha, sadly time spent in blockbench doesn’t count as it doesn’t have a plugin for wakatime or whatever :/ (the 43min this devlog logs was actually setting up oxipng to automatically run on every commit to optimize the png files) Next i guess will be to make an actual texture for it, yay. Here’s the super very interesting texture that currently just has all the different mapped sides in different colors"}, {"post_id": 44754, "ts": "2026-08-10T18:48:32Z", "hours": 2.0067, "text": "Started working on the soul spikes. First made a model for it, not sure if it’ll look good once I make a texture for the actual spikes part of it as well"}, {"post_id": 41521, "ts": "2026-08-05T19:39:30Z", "hours": 1.7181, "text": "Been doing some boring things like writing a description and making an icon and some miscellaneous fixes and whatnot as I actually think I want to get this in a state where I could send it in for review on modrinth. Otherwise if I sent it in once I was actually finished with the project, it probably wouldn’t get reviewed before stardance ends and then i’d have to tell people to download the mod from my git releases and that wouldn’t automatically tell em where to get dependencies and whatnot so yeah, want to get it in the queue asap. Here’s the icon I came up with btw"}, {"post_id": 40668, "ts": "2026-08-04T11:02:42Z", "hours": 2.4575, "text": "Added a texture and recipe to item collector, made it possible to disable it using redstone, made the sample collector only pick up items every 10 ticks."}, {"post_id": 39730, "ts": "2026-08-02T21:36:20Z", "hours": 1.855, "text": "Shift-right-clicking an item collector with an empty hand now shows its range. Drawing lines of particles between the correct corners for the outline took a bit of trial and error, also added a screenshot where it doesn’t look quite correct Also I’m not sure if the video from the previous devlog got uploaded correctly (my browser at least isn’t able to play it) so here it is again"}, {"post_id": 39564, "ts": "2026-08-02T16:40:59Z", "hours": 2.9042, "text": "Made item collector spawn a line of particles when picking up items Oh and also added compatibility to item collector with container blocks from other mods that use the fabric or neoforge transfer api instead of vanilla containers, more loader specific stuff, yay!"}, {"post_id": 38906, "ts": "2026-08-01T14:21:22Z", "hours": 1.0933, "text": "Implemented basic functionality of the item collector. Currently has a range of 2 blocks that it will pick things up from and put em straight into a connected inventory"}, {"post_id": 38761, "ts": "2026-08-01T10:08:52Z", "hours": 3.6756, "text": "Started work on adding the item collector, currently just got done with adding the block itself with some boilerplate stuff and a model. As the name suggests, it’ll be for picking up items in some range of it. Not quite sure if I want it to be a range all around the block, or maybe going outwards from the direction it’s facing? Also not yet quite sure if I want it to just put stuff into an internal inventory and require a hopper or something modded to pull out of it, or if it should act like a hopper pushing things out from the side it’s attached on"}, {"post_id": 37603, "ts": "2026-07-30T13:35:05Z", "hours": 1.1933, "text": "Received new textures and spent some time messing around in the mcmeta file for the animation timing. Looks quite good now. Also made the empty soul gem use the same texture, just with a soul light blue colors."}, {"post_id": 37190, "ts": "2026-07-29T18:31:37Z", "hours": 5.0456, "text": "Past while has been spent on fighting with gradle to extend my overly complicated setup ( monkeylogic, my gradle convention plugin ) and make it even more complicated allow adding dependencies from modrinth to projects. After that I could easily add RRV (Reliable Recipe Viewer) as a dependency and now lastly add integration with it. Currently I’ve got integration to the point of showing the custom duplication recipe, which is great"}, {"post_id": 35160, "ts": "2026-07-26T12:28:59Z", "hours": 2.7197, "text": "Finally fixed the soul gem duplication recipe, which was a bit of a pain to deal with… I initially went down a dumb rabbit hole of overcomplicating the matcher system when turns out having multiple of the same item in the recipe can easily be solved by storing a count for each ingredient instead of having multiple entries for them 😝"}, {"post_id": 34287, "ts": "2026-07-24T21:18:42Z", "hours": 2.5044, "text": "Made spawners craftable, made interacting with spawners using a filled soul gem set the entity in the spawner. Still not quite sure what to call the “filled” soul gem. One idea i had was to call it just a soul gem and call the not filled one an “Empty Soul Gem”, which is honestly a pretty good idea, but I’m not sure what to do when it’s in the process of being filled. Technically at that point it’s not empty , but maybe it’d be fine to call it that"}, {"post_id": 32839, "ts": "2026-07-22T07:47:33Z", "hours": 3.5317, "text": "Lots of fun stuff between this and the last devlog: Creative tab for filled soul gems now only contains supported ones (had to make creative tabs refresh when data is reloaded cause datapacks can change what’s supported) Removed the old SampleTakerItem and FilledSampleTakerItem classes and moved most logic to custom components or mixins. Soul gems can be filled by holding them in the off hand when killing the target entity ( https://imgur.com/a/Lz6PjZs ) Some optimizations"}, {"post_id": 32407, "ts": "2026-07-21T16:00:26Z", "hours": 1.9203, "text": "Optimized the EntityTiers lookup (which I mentioned I messed up last devlog) by using a map instead of a list, and it’s also now storing a reference to the Identifier instead of EntityType, so there’s less work converting between them for the packet codec (has to be sent as an Identifier (or, well, string) over the network) and also when doing some of the lookups (like from the tint provider, which has just the id by itself) Not sure what to put as the screenshot it requires me to add…. I guess have some prototypes of the new soul gem texture a person’s working on (the final shape will probably not be a perfectly round pearl)"}, {"post_id": 32271, "ts": "2026-07-21T10:39:38Z", "hours": 3.1261, "text": "Made “filled” soul gems have colors based on the entity they contain! (and also switched to a different (and probably even worse lmao) texture) The colors are the same as what spawn eggs used to have, thankfully the minecraft wiki has a list of what they all were before that was removed. Sadly I can’t steal the entity colors from vanilla anymore as that was removed in 1.21.5, so I had to add color fields to the entity tiers config and that messed with a lot of stuff and is a bit of a hit on performance when it’s not looping over stuff in weird ways as it’s a list of entries when it really should probably be a map… I’ll fix that i guess Also I know this texture is ass and someone a bit more talented in drawing is cooking up a new one which I’ll probably be able to show off in a few days. Question is… do I want the current one to show up when the vanilla programmer art pack is enabled or is that too embarrassing to show off?"}, {"post_id": 31810, "ts": "2026-07-20T14:25:41Z", "hours": 0.9456, "text": "Added recipes, translations, etc for soul essence and gem, removed spirit bottle, added recipe for spawner, made spawners drop with silk touch. Thanks to neoforge there have to be separate mixins for fabric and neo for disabling the xp drop when a spawner is broken with silk touch (would lead to infinite xp otherwise lol)…. if only neoforge was just fabric"}, {"post_id": 31751, "ts": "2026-07-20T12:04:50Z", "hours": 1.2917, "text": "Made soul essence drop from mobs killed on soul sand/soil. Also spawns fun particles! https://imgur.com/a/Qt5XxUI"}, {"post_id": 31695, "ts": "2026-07-20T09:44:19Z", "hours": 0.3378, "text": "So I’ve been doing some brainstorming and writing things down on paper (like a caveman) and have at least redesigned some of the features that already exist. Instead of a bottle of spirits that you get by right clicking a spawner, there’s going to be Soul Essence that you get by killing a mob while they’re standing on soul sand/soil. Instead of the Sample taker, there’s gonna be a soul gem crafted from a diamond and 4 soul essence that will be filled by holding it in your offhand while killing x amount of the target mob. Also thought of some recipes that I’m not gonna explain here rn. Sadly (or thankfully) I haven’t found a wakatime plugin for Aseprite, so nobody will know just how many hours I spent on the absolute trash textures I made for the soul gem and soul essence (I promise they’ll be better at some point maybe idk if I can do any better 😭 ). Soul essence just looks off somehow, and the soul gem looks like a duck spawn egg 😭 😭 😭 😭"}, {"post_id": 31122, "ts": "2026-07-19T08:32:28Z", "hours": 6.5108, "text": "It is now working on NeoForge as well! Getting the inventory to work with the neoforge transfer api was a bit of a pain, but I managed to get it to work! (Don’t mind the debug upgrades in the screenshot, they make it easier to test but dont have textures) Next up is uhh… some brainstorming and rethinking how the whole mod works and thinking about what’s in scope for this and what’s not: One thing I definitely want to add is a crafting recipe for spawners and making the sample takers (which will probably be renamed to something relating to souls and gems) usable as spawn eggs on spawners. I’m not quite sure if upgradeable spawners should be a part of this though as there’s already apotheosis for neoforge (and a port in the works for fabric) that does that and probably other mods as well. I also really wanted to add things for moving and killing mobs in the world, for which there are also already mods like Dark Utilities (got quite inspired by that) which afaik also is updated to the latest version and is for both fabric and neoforge…. Though it seems to have more random things unrelated to mob farming, and maybe it’d be fine if I really differentiate from it and other similar mods with the theme of this? Yeahhh this’ll need quite a bit more thinking."}, {"post_id": 28545, "ts": "2026-07-14T21:20:48Z", "hours": 2.0633, "text": "Finished getting everything out of :loader:fabric and into :modded that makes sense to move. The fabric subproject now only has 4 files, two of which are just the entrypoints that invoke the common initializer and the other two are using fabric api specific things and can’t be moved out without a lot of headache!"}, {"post_id": 28177, "ts": "2026-07-14T09:44:23Z", "hours": 0.2531, "text": "(the 15m were spent working on getting MenuProvider stuff out from fabric specific code and into the common modded subproject, butttt I’m making a post cause I wanna share…) Some funny screenshots from when I was porting from 1.21.1 to 26.1 (rendering changes here are a pain lol)"}, {"post_id": 28170, "ts": "2026-07-14T09:24:59Z", "hours": 17.9675, "text": "Just realized this is probably something I could submit to stardance as well 😅 Basically the initial idea was to have a mob farm contained in a single block. You craft a sample taker and have to click on 10 separate instances of an entity to get a filled sample of that entity. Then you put that into a Compact Mob Farm, turn it on, maybe give it some upgrades and a sword, and it’ll start farming the drops of the entity you took samples of. Here are some screenshots of what stuff looks like"}]...
|
Title: Ultimate Mob Farms by @OffsetMonkey538 | St Title: Ultimate Mob Farms by @OffsetMonkey538 | Stardance
Devlogs:
Just realized this is probably something I could submit to stardance as well 😅 Basically the initial idea was to have a mob farm contained in a single block. You craft a sample taker and have to click on 10 separate instances of an entity to get a filled sample of that entity. Then you put that into a Compact Mob Farm, turn it on, maybe give it some upgrades and a sword, and it’ll start farming the drops of the entity you took samples of. Here are some screenshots of what stuff looks like
(the 15m were spent working on getting MenuProvider stuff out from fabric specific code and into the common modded subproject, butttt I’m making a post cause I wanna share…) Some funny screenshots from when I was porting from 1.21.1 to 26.1 (rendering changes here are a pain lol)
Finished getting everything out of :loader:fabric and into :modded that makes sense to move. The fabric subproject now only has 4 files, two of which are just the entrypoints that invoke the common initializer and the other two are using fabric api specific things and can’t be moved out without a lot of headache!
It is now working on NeoForge as well! Getting the inventory to work with the neoforge transfer api was a bit of a pain, but I managed to get it to work! (Don’t mind the debug upgrades in the screenshot, they make it easier to test but dont have textures) Next up is uhh… some brainstorming and rethinking how the whole mod works and thinking about what’s in scope for this and what’s not: One thing I definitely want to add is a crafting recipe for spawners and making the sample takers (which will probably be renamed to something relating to souls and gems) usable as spawn eggs on spawners. I’m not quite sure if upgradeable spawners should be a part of this though as there’s already apotheosis for neoforge (and a port in the works for fabric) that does that and probably other mods as well. I also really wanted to add things for moving and killing mobs in the world, for which there are also already mods like Dark Utilities (got quite inspired by that) which afaik also is updated to the latest version and is for both fabric and neoforge…. Though it seems to have more random things unrelated to mob farming, and maybe it’d be fine if I really differentiate from it and other similar mods with the theme of this? Yeahhh this’ll need quite a bit more thinking.
So I’ve been doing some brainstorming and writing things down on paper (like a caveman) and have at least redesigned some of the features that already exist. Instead of a bottle of spirits that you get by right clicking a spawner, there’s going to be Soul Essence that you get by killing a mob while they’re standing on soul sand/soil. Instead of the Sample taker, there’s gonna be a soul gem crafted from a diamond and 4 soul essence that will be filled by holding it in your offhand while killing x amount of the target mob. Also thought of some recipes that I’m not gonna explain here rn. Sadly (or thankfully) I haven’t found a wakatime plugin for Aseprite, so nobody will know just how many hours I spent on the absolute trash textures I made for the soul gem and soul essence (I promise they’ll be better at some point maybe idk if I can do any better 😭 ). Soul essence just looks off somehow, and the soul gem looks like a duck spawn egg 😭 😭 😭 😭
Made soul essence drop from mobs killed on soul sand/soil. Also spawns fun particles! https://imgur.com/a/Qt5XxUI
Added recipes, translations, etc for soul essence and gem, removed spirit bottle, added recipe for spawner, made spawners drop with silk touch. Thanks to neoforge there have to be separate mixins for fabric and neo for disabling the xp drop when a spawner is broken with silk touch (would lead to infinite xp otherwise lol)…. if only neoforge was just fabric
Made “filled” soul gems have colors based on the entity they contain! (and also switched to a different (and probably even worse lmao) texture) The colors are the same as what spawn eggs used to have, thankfully the minecraft wiki has a list of what they all were before that was removed. Sadly I can’t steal the entity colors from vanilla anymore as that was removed in 1.21.5, so I had to add color fields to the entity tiers config and that messed with a lot of stuff and is a bit of a hit on performance when it’s not looping over stuff in weird ways as it’s a list of entries when it really should probably be a map… I’ll fix that i guess Also I know this texture is ass and someone a bit more talented in drawing is cooking up a new one which I’ll probably be able to show off in a few days. Question is… do I want the current one to show up when the vanilla programmer art pack is enabled or is that too embarrassing to show off?
Optimized the EntityTiers lookup (which I mentioned I messed up last devlog) by using a map instead of a list, and it’s also now storing a reference to the Identifier instead of EntityType, so there’s less work converting between them for the packet codec (has to be sent as an Identifier (or, well, string) over the network) and also when doing some of the lookups (like from the tint provider, which has just the id by itself) Not sure what to put as the screenshot it requires me to add…. I guess have some prototypes of the new soul gem texture a person’s working on (the final shape will probably not be a perfectly round pearl)
Lots of fun stuff between this and the last devlog: Creative tab for filled soul gems now only contains supported ones (had to make creative tabs refresh when data is reloaded cause datapacks can change what’s supported) Removed the old SampleTakerItem and FilledSampleTakerItem classes and moved most logic to custom components or mixins. Soul gems can be filled by holding them in the off hand when killing the target entity ( https://imgur.com/a/Lz6PjZs ) Some optimizations
Made spawners craftable, made interacting with spawners using a filled soul gem set the entity in the spawner. Still not quite sure what to call the “filled” soul gem. One idea i had was to call it just a soul gem and call the not filled one an “Empty Soul Gem”, which is honestly a pretty good idea, but I’m not sure what to do when it’s in the process of being filled. Technically at that point it’s not empty , but maybe it’d be fine to call it that
Finally fixed the soul gem duplication recipe, which was a bit of a pain to deal with… I initially went down a dumb rabbit hole of overcomplicating the matcher system when turns out having multiple of the same item in the recipe can easily be solved by storing a count for each ingredient instead of having multiple entries for them 😝
Past while has been spent on fighting with gradle to extend my overly complicated setup ( monkeylogic, my gradle convention plugin ) and make it even more complicated allow adding dependencies from modrinth to projects. After that I could easily add RRV (Reliable Recipe Viewer) as a dependency and now lastly add integration with it. Currently I’ve got integration to the point of showing the custom duplication recipe, which is great
Received new textures and spent some time messing around in the mcmeta file for the animation timing. Looks quite good now. Also made the empty soul gem use the same texture, just with a soul light blue colors.
Started work on adding the item collector, currently just got done with adding the block itself with some boilerplate stuff and a model. As the name suggests, it’ll be for picking up items in some range of it. Not quite sure if I want it to be a range all around the block, or maybe going outwards from the direction it’s facing? Also not yet quite sure if I want it to just put stuff into an internal inventory and require a hopper or something modded to pull out of it, or if it should act like a hopper pushing things out from the side it’s attached on
Implemented basic functionality of the item collector. Currently has a range of 2 blocks that it will pick things up from and put em straight into a connected inventory
Made item collector spawn a line of particles when picking up items Oh and also added compatibility to item collector with container blocks from other mods that use the fabric or neoforge transfer api instead of vanilla containers, more loader specific stuff, yay!
Shift-right-clicking an item collector with an empty hand now shows its range. Drawing lines of particles between the correct corners for the outline took a bit of trial and error, also added a screenshot where it doesn’t look quite correct Also I’m not sure if the video from the previous devlog got uploaded correctly (my browser at least isn’t able to play it) so here it is again
Added a texture and recipe to item collector, made it possible to disable it using redstone, made the sample collector only pick up items every 10 ticks.
Been doing some boring things like writing a description and making an icon and some miscellaneous fixes and whatnot as I actually think I want to get this in a state where I could send it in for review on modrinth. Otherwise if I sent it in once I was actually finished with the project, it probably wouldn’t get reviewed before stardance ends and then i’d have to tell people to download the mod from my git releases and that wouldn’t automatically tell em where to get dependencies and whatnot so yeah, want to get it in the queue asap. Here’s the icon I came up with btw
Started working on the soul spikes. First made a model for it, not sure if it’ll look good once I make a texture for the actual spikes part of it as well
Finished UV mapping the spikes…. this literally took like over an hour haha, sadly time spent in blockbench doesn’t count as it doesn’t have a plugin for wakatime or whatever :/ (the 43min this devlog logs was actually setting up oxipng to automatically run on every commit to optimize the png files) Next i guess will be to make an actual texture for it, yay. Here’s the super very interesting texture that currently just has all the different mapped sides in different colors
Made an okay-ish texture for the soul spikes block (many of em at once looks kinda noisy but oh well, it’ll do. Hopefully it’ll be locked up in a farm killing chamber room anyway). Had to change the UV mapping again because for whatever reasons, minecraft decides to render a sliver of the neighboring pixel as well which looks super bad with two very contrasty colors next to each other. Solution was to move the blue parts to the bottom of the texture where there was room to have it separated by a row of blank pixels.
Made the soul spikes actually deal extra fall damage when fell on. Current fall damage multiplier is at 5, not sure if that is too much or too little. The damage is done by a FakePlayer, so the mob will also drop loot that only drops from player kills, including experience orbs. (Seems like I need to add an XP Collector block in the future to deal with those). Oh and also did some boilerplate-ish things to soul spikes like adding a recipe, translation, etc Turns out minecraft decides that the block you fall on is the one that’s 0.2 blocks below you…. The model of the soul spikes (which i don’t really wanna change anymore) has the base at 3 pixels high, which makes it… 0.1875 block high 😮💨 Nothing that can’t be solved with a mixin to Entity#getOnPosLegacy that changes the BlockPos it returns if the the block above the “correct” block is soul spikes
Added the experience collector block, which obviously picks up xp orbs and turns em into bottles of enchanting. Kind of got so focused I forgot about making more detailed devlogs 😅
Spent some time thinking about what I’m gonna do for the block that moves mobs around. I think I’ve decided on going with a basic “fan” type block and now started working on what it could look like. I’m imagining the souls kinda like escaping from the soul sand and also particles spawning and moving in the direction it’s pushing. As always I’m pretty bad at drawing textures, so the beginning here looks a bit crude, but it’s definitely a start. It’d be really nice if aseprite had a wakatime plugin…
Spent a bunch of time on cleaning up some code and abstracting some things out of collector block classes to make it easier to add functionality of the soul fan later on, speaking of which! the soul fan is a block that exists in game now
Spent the last hour and a half faffing about with my loader abstraction in monkeylib (stardance only recorded an hour cause that’s technically a separate project) for creative tabs. All that just cause I really wanted the RRV stack group for the filled soul gems to show up next to the empty soul gem, instead of at the end of the list where it did previously
Implemented functionality of the soul fan, getting the strength to scale based on distance was a bit of a pain cause i needed to get the position of the center of the face that the fan’s pointing in and only calculate the distance to the entity based on the correct axis cause otherwise giving it a larger range would make the farther to the side entities get pushed less and uhh yeah, took quite a bit of experimenting with stuff to get it to work correctly
Added range upgrades for the fan and collector blocks. It was a bit challenging figuring out actually extending the bounding box in the correct directions based on the block is facing, but I did get it working
Added particle effects for soul fan. Figuring out where to spawn em was quite fun actually, though I did have to add a custom particle (reuses the vanilla “small gust” texture) because turns out most particles won’t obey the velocity you give them and just do their own thing, which was a bit annoying.
Made soul fans placed at different locations use a different starting frame offset, so they don’t all look the same (see before and after videos). The way this is implemented is a bit interesting… There’s no easy “use this model but offset any texture’s animation by 1 frame” as afaik the frame index is global per texture. What Minecraft does allow is each block state variant actually being an array of variants that the game will choose between using afaik a hash derived from the block position. This is used in vanilla for things like stone and deepslate, which have mirrored and rotated variants so caves look less repetitive or whatever. As each texture has a global frame index, I am duplicating the soul fan texture and just changing the order of the frames in the accompanying animation metadata file and creating separate models which point to the different textures and then those models are used as variants in the blockstate. What’s even more fun is I am using datagen for duplicating the texture and creating the animation metadata files, which definitely isn’t concerning at all. This also required me to make a pull request to fabric api because the FabricCodecDataProvider assumes all files it generates are .json files, but animation metadata is .mcmeta. (The PR isn’t yet merged so I resorted to…. injecting a mixin into fabric api, definitely not cursed at all I swear)
Bunch of refactoring, bug fixes, balancing, etc. Release notes can be seen on the forgejo release or on modrinth, I’m not gonna copy paste em here (this is version 0.0.5 right now) It is now actually reviewed on Modrinth, took two weeks for that to happen (Gosh I remember when review times were like less than 24 hours ). I’m still keeping it unlisted until I get a 1.0.0 release out, but if anyone wants to check it out and share feedback or anything, then it’s right over here: https://modrinth.com/mod/ultimate-mob-farms Before a v1, I still need better rrv and item descriptions integration, some more balancing changes, advancements, quite a few bugfixes and probably some other stuff I’m forgetting at the moment.
Fixed compact mob farm inventory not working correctly and being incompatible with other mods’ item movement stuff. The whole point of the transfer API on both fabric and neoforge is the transaction system, where each change must store a snapshot that can be returned to if needed; Turns out ignoring the TransactionContext and not saving any snapshots is a bad idea 😅 I’ve tested and it’s compatible with create fly (see screenshot where a compact mob farm with a blaze is being used to feed a blaze burner for a steam engine) and most probably other mods as well. Added Reliable Recipe Viewer integration that explains soul capturing
checking the recipe for a specific soul gem now only shows that specific entity instead of all soul capturing recipes soul capturing recipe now shows up before the duplication recipe soul gems can now be searched by the name of the entity they contain fix compact mob farm gui not rendering vanilla background overlay allow filling soul gem when held in the main hand fix dedicated server crashing Running in a dedicated server would crash because there are quite a few places where I referenced client-only classes in places that the jvm will load on the dedicated server as well. Mostly in the different block entity classes, where I had a clientTick method that had ClientLevel as one of it’s parameters. Even though that method would never get called on the dedicated server, java still wants it to exist when loading the class because it’s part of a method’s definition. My current solution is very very awful… I moved those things into methods in the client initializer and… well… yeah… it just looks and is awful: https://git.offsetmonkey538.top/OffsetMods538/Ultimate-Mob-Farms/src/commit/0.0.7/modded/src/main/java/top/offsetmonkey538/ultimatemobfarms/modded/UltimateMobFarmsModdedClient.java#L65 The real, foolproof solution would be using the split sources feature of loom, which creates a second source set and makes the main source set unable to access client classes. Only problem is that it’d be a bit annoying to fully get set up in my multiloader-version gradle system and would most definitely need many changes on MonkeyLib538 as well and I just am not up for doing that atm.
Getting closer and closer to the actual release… could this be the last devblog before that? Still not quite 100% about if the required kills for each mob are balanced, but it’s probably good enough. Definitely better than everything being 10 kills tho. Felt very off-balance when withers, which are hard to spawn and hard to kill, and pigs, which are just everywhere, required the same amount of kills to capture. Notable changes: Show soul capturing recipe in rrv when checking usages of spawn eggs Show tooltip with entity name when hovering entity in soul capturing rrv recipe Add item descriptions Fix being able to insert items into the compact mob farm inventory Make amount of kills required for capturing an entity different per entity. Update compact mob farm recipe (now uses soul spikes in the middle instead of stone bricks) Store soul gem colors as hex strings in entity tiers config. Much more understandable and easier to manually change the overrides config or whatever Make built-in packs use the mod icon using a mixin instead of copying the icon into the packs. This shaves like 80kb off the final jar which is quite a bit of unnecessary duplicate crap Remove some log spam that was for debugging
School starting with september really hit my productivity on this mod like a truck I am realizing the deadline for stardance is getting real close, so I think this is probably the point where I’ll have to ship it. There are still some TODOs in the code and bugs that need to be squashed and porting to 26.3 (which released like this week, thanks mojang ) and so I haven’t been comfortable marking the latest release as 1.0.0 yet or making announcements elsewhere, but the mod is now perfectly usable and I like where it is. Changes since last devblog: 26.2 support Injecting some items into vanilla loot chests Advancements 26.2 wasn’t actually as painful as I thought it’d be, which was a nice surprise. Adding advancements was a bit more of a pain with 26.2 support though as I needed custom criteria for some of the advancements (one for putting a soul gem in the compact mob farm and another for using a soul gem on a spawner) and Mojang, in their infinite wisdom, decided to move a bunch of advancement stuff around in 26.2.
Waow that’s quite a bit of stardust 😅 Quite happy with this project and the ratings it got on the first ship :D (except the one that accused my devlogs of being written by ai… My writing is not that bad, right? ) I noticed in the shipwrights’ review video that how upgrades work is not very obvious, they were trying to put a tier upgrade into a soul fan, which only accepts width, height and depth upgrades. It was mentioned in ratings that stuff is a bit hard to figure out in the beginning as well. So to fix that, I added some silhouettes of the supported upgrades shown in the slots and also a hover tooltip saying what’s supported. Oh and i’ve also added 26.3 support now
Added a progress bar to the Compact Mob Farm GUI that hopefully makes it much easier to understand when it’ll damage the entity and it doesn’t look like it’s just doing nothing lol, especially with the 30 second default time
Added an RRV screen showing what items can be acquired from the Compact Mob Farm. Shows the entity, the items it drops and the required tier upgrade level. Hoping this also helps with confusion on how stuff works
Started working on setting up a docs page for the mod. Mainly want it for explaining the data driven configuration, but having full pages to give more detailed explanations of the different features is also probably a nice touch. Made a new icon for the mod as well, it’s pretty much the same but higher res because the 256x256 one looked weird on the docs page. The docs are done using Starlight like all my other docs pages (see those here ) and lives at https://ultimate-mob-farms.docs.cursedmods.com/ and on the docs/master branch in the repo Currently it’s just the landing page, but it’ll have more stuff soon enough
Ships:
This is a Minecraft mod for making mob farms, something similar to for example Mob Grinding Utils. It features item and xp collection, entity movement, spawner relocation and crafting, single block mob farms and some other stuff. Honestly, I’m quite proud just over the fact that I finally did finish this project (or at least got it to the point of being comfortable releasing it publically) and it’s hard to choose any specific features, but here I go… I do really like how the particle effects for the soul fan turned out, they’re quite satisfying imo. This is kind of unrelated to the mod, but I’m also proud of having helped out with the development of RRV, the recipe viewer mod I added compat with. While adding support for it in this mod, I noticed some bugs and crashes and stuff, which I PRed fixes for. It’s a bigger project with many complex things, so coming up with a list of all the things I found hard or took a lot of time and explaining all of them would probably go over whatever character limit this text box has lol. One thing that was a big PITA was the compact mob farm inventory. The original implementation I wrote was just… completely wrong, so I had to rewrite it. Then later I realized I overlooked like the most important part of the item transfer api (transactions, which are used for rolling back changes and stuff) so I had to rewrite it again, and then after that I rewrote quite a bit of it one more time because, after the last rewrite, I had accidentally made it possible for other blocks to insert into the inventory, which isn’t something I wanted as that seems quite OP. Another thing that comes to mind is figuring out the code for the soul fan’s range based on upgrades and strength at a given distance. The problem was that the coordinate you have to increase/decrease depends on the orientation of the block, so as an example, the depth upgrade would need to increase the Y pos when the block is placed pointing up and decrease it when pointing down, the X pos when facing north/south and the Z pos when facing east/west. For pushing the entities, I had to find the coordinates of the middle of the face the block is pointing, and add/subtract the correct coordinate to get the range in the depth direction, and then find the distance between each entity currently in range and the fan in just that plane for calculating the strength. For people testing out the project, I definitely do recommend also installing the Reliable Recipe Viewer (RRV) and Item Description mods...
|
1792853443.0379903
|
1792939843.0379903
|
|
46459
|
APStudyCanvas by @derek_chen | Stardance
|
derek_chen
|
1
|
139.83
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-17T01:01:11Z", "hours": 133.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "APStudyCanvas’s first beta release improves Canvas by improving the dashboard, a much richer To-Do experience, improved grades and course cards, synchronized streak tracking, smoother motion, and the first Google Calendar integration through Nest.APStudy. This extension adds onto CanvasRefined which similarly started due to dislike of BetterCampus’s high extension costs. Please note that this is still an early stage of the extension and bugs/optimization issues will be present. If you see any, please don’t hesitate to include comments in your feedback! They are greatly appreciated. The image below is a preview of the extension popup.", "repo": "https://github.com/SimpleLemon/APStudyCanvas", "demo": "https://youtu.be/wb6AkZ6M4Q8"}]...
|
[{"post_id": 71578, "ts": "202 [{"post_id": 71578, "ts": "2026-09-21T02:45:37Z", "hours": 0.3953, "text": "Release: Beta 0.6 Link"}, {"post_id": 71562, "ts": "2026-09-21T02:00:02Z", "hours": 2.6711, "text": "Added a new orbital progress “bar” option that you can move around in 3d. I’m really happy with how it turned out."}, {"post_id": 70943, "ts": "2026-09-20T02:41:38Z", "hours": 3.76, "text": "Genuinely spent 2 hours on improving this 😭 Any design suggestions? I went with a compact streak button in the same line as the To-Do List instead of a large section above it to give more screen space to the assignments list. The large center is an egg surrounded by fire (because the extension is built under Nest.APStudy. So nests –> eggs) The motivational message also has a fun easter egg: one exclamation mark is rare and two exclamation mark messages are ultra rare. Just something to look forward to everyday."}, {"post_id": 69421, "ts": "2026-09-17T00:57:48Z", "hours": 2.2194, "text": "Still doesn’t look quite there yet, but more design UI will go into this including graphs and previous assignment history and how it affects your overall course grade."}, {"post_id": 68251, "ts": "2026-09-14T19:43:05Z", "hours": 8.8794, "text": "Improvement on the design! What do we think? Better rainbow, better sidebar, better assignment details, better streak, better due list, etc."}, {"post_id": 66008, "ts": "2026-09-11T15:02:04Z", "hours": 7.2456, "text": "Working on moving the 4 pages (Grades, Planner, Notes, and Study) to the popup UI and making it look nice."}, {"post_id": 64480, "ts": "2026-09-08T22:10:08Z", "hours": 3.4811, "text": "Settings design update! Went with an ios settings design"}, {"post_id": 63946, "ts": "2026-09-08T01:05:50Z", "hours": 8.5072, "text": "Looking better now! Now time to get some features to actually work properly (ie the 4 extension added pages)"}, {"post_id": 63211, "ts": "2026-09-06T21:51:20Z", "hours": 11.9767, "text": "Mildly messed up the settings page :("}, {"post_id": 61898, "ts": "2026-09-05T02:12:56Z", "hours": 9.4814, "text": "Heavily touched up that todo section & card appearance. Still got lots of issues to work on.As for new features, I’m working on adding canvas search, grade analytics, dashboard notes, local themes, planner tasks, and a better sidebar.Given that this project was initially a fork of CanvasRefined (though it has diverged by a large amount), I’m working on adding the new features/bug fixes to this extension as well."}, {"post_id": 60335, "ts": "2026-09-02T16:49:27Z", "hours": 9.1958, "text": "AYYY its coming together! The cards are now loading properly, the settings actually controls stuff, and the rings are coming together. Though, I can’t seem to figure out how to hide courses without active assignments from the ring design. After I finish all the UI changes I can think of, I’ll go through and completely revamp the settings popup (it’s mad ugly right now)."}, {"post_id": 58999, "ts": "2026-08-31T16:04:32Z", "hours": 12.4089, "text": "I may have broken the todo list section… But I’m still working on it and improving the design. I going for a rainbow style layout with a 3 column toggle for assignments and announcements"}, {"post_id": 55680, "ts": "2026-08-27T17:22:38Z", "hours": 8.4667, "text": "Sidebar is still coming together, but we’re getting closer to a nice design now!"}, {"post_id": 54401, "ts": "2026-08-25T12:19:14Z", "hours": 12.7903, "text": "Still working on improving the sidebar, which is suprisingly difficult to make look good. As for the todo list bar, I’m hoping to make it more like a rainbow and the tasks list to be easier to read."}, {"post_id": 49880, "ts": "2026-08-18T20:38:13Z", "hours": 9.9878, "text": "I’m trying to create a design similar to this… Still a work in progress though My Canvas page now looks broken with my WIP design, which is not good :("}, {"post_id": 46904, "ts": "2026-08-14T13:27:52Z", "hours": 10.6172, "text": "Almost done with my big change!"}, {"post_id": 46153, "ts": "2026-08-13T05:12:48Z", "hours": 8.9219, "text": "Many changes coming soon! Update in the DevLog above!"}, {"post_id": 45847, "ts": "2026-08-12T16:00:24Z", "hours": 9.2033, "text": "I am starting a project by forking CanvasRefined and connecting it to Nest.APStudy.org (my productivity project), as well as adding new features to make it easier to use myself. Improvements: Rebranded the extension (whilst keeping previous creator credit). Replaced the old settings screen with the new Edit Canvas workspace and compact-to-expanded navigation (essentially redesigned the popup and settings UI with clearer categories, spacing, focus states, and reduced-motion support) Added settings search with keyboard shortcuts and accessible live status updates. Improved course-card editing with previews, custom names, codes, images, visibility controls, saving, and reset actions. Added a settings-migration runbook for moving data from CanvasRefined to APStudyCanvas. Move changes to come!"}]...
|
Title: APStudyCanvas by @derek_chen | Stardance
D Title: APStudyCanvas by @derek_chen | Stardance
Devlogs:
I am starting a project by forking CanvasRefined and connecting it to Nest.APStudy.org (my productivity project), as well as adding new features to make it easier to use myself. Improvements: Rebranded the extension (whilst keeping previous creator credit). Replaced the old settings screen with the new Edit Canvas workspace and compact-to-expanded navigation (essentially redesigned the popup and settings UI with clearer categories, spacing, focus states, and reduced-motion support) Added settings search with keyboard shortcuts and accessible live status updates. Improved course-card editing with previews, custom names, codes, images, visibility controls, saving, and reset actions. Added a settings-migration runbook for moving data from CanvasRefined to APStudyCanvas. Move changes to come!
Many changes coming soon! Update in the DevLog above!
Almost done with my big change!
I’m trying to create a design similar to this… Still a work in progress though My Canvas page now looks broken with my WIP design, which is not good :(
Still working on improving the sidebar, which is suprisingly difficult to make look good. As for the todo list bar, I’m hoping to make it more like a rainbow and the tasks list to be easier to read.
Sidebar is still coming together, but we’re getting closer to a nice design now!
I may have broken the todo list section… But I’m still working on it and improving the design. I going for a rainbow style layout with a 3 column toggle for assignments and announcements
AYYY its coming together! The cards are now loading properly, the settings actually controls stuff, and the rings are coming together. Though, I can’t seem to figure out how to hide courses without active assignments from the ring design. After I finish all the UI changes I can think of, I’ll go through and completely revamp the settings popup (it’s mad ugly right now).
Heavily touched up that todo section & card appearance. Still got lots of issues to work on.As for new features, I’m working on adding canvas search, grade analytics, dashboard notes, local themes, planner tasks, and a better sidebar.Given that this project was initially a fork of CanvasRefined (though it has diverged by a large amount), I’m working on adding the new features/bug fixes to this extension as well.
Mildly messed up the settings page :(
Looking better now! Now time to get some features to actually work properly (ie the 4 extension added pages)
Settings design update! Went with an ios settings design
Working on moving the 4 pages (Grades, Planner, Notes, and Study) to the popup UI and making it look nice.
Improvement on the design! What do we think? Better rainbow, better sidebar, better assignment details, better streak, better due list, etc.
Still doesn’t look quite there yet, but more design UI will go into this including graphs and previous assignment history and how it affects your overall course grade.
Genuinely spent 2 hours on improving this 😭 Any design suggestions? I went with a compact streak button in the same line as the To-Do List instead of a large section above it to give more screen space to the assignments list. The large center is an egg surrounded by fire (because the extension is built under Nest.APStudy. So nests –> eggs) The motivational message also has a fun easter egg: one exclamation mark is rare and two exclamation mark messages are ultra rare. Just something to look forward to everyday.
Added a new orbital progress “bar” option that you can move around in 3d. I’m really happy with how it turned out.
Release: Beta 0.6 Link
Ships:
APStudyCanvas’s first beta release improves Canvas by improving the dashboard, a much richer To-Do experience, improved grades and course cards, synchronized streak tracking, smoother motion, and the first Google Calendar integration through Nest.APStudy. This extension adds onto CanvasRefined which similarly started due to dislike of BetterCampus’s high extension costs. Please note that this is still an early stage of the extension and bugs/optimization issues will be present. If you see any, please don’t hesitate to include comments in your feedback! They are greatly appreciated. The image below is a preview of the extension popup....
|
1792853716.4861097
|
1792940116.4861097
|
|
6517
|
swarmtrace by @raviiiibro | Stardance
|
raviiiibro
|
1
|
140.0
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-07T14:12:02Z", "hours": 140.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "the marketing was challenging and i think now it is ready and i get more suggestion if people will use it and i hope i ccan buy a domain by the star dust", "repo": "https://github.com/ravi3594444/swarmtrace", "demo": "https://swarmtrace.vercel.app/"}]...
|
[{"post_id": 57987, "ts": "202 [{"post_id": 57987, "ts": "2026-08-30T10:07:47Z", "hours": 3.3442, "text": "half work done"}, {"post_id": 45694, "ts": "2026-08-12T10:51:40Z", "hours": 14.5639, "text": "starting work"}, {"post_id": 38796, "ts": "2026-08-01T11:14:26Z", "hours": 10.8114, "text": "today i know there is so much work left on this project then i think i added a floating side bar added some more now features for backend optimization guys working on a i3 sucks please help me to buy an macbook byyy"}, {"post_id": 34749, "ts": "2026-07-25T18:41:00Z", "hours": 9.1911, "text": "adding agent graph this is mvp only i will polish it byee"}, {"post_id": 31855, "ts": "2026-07-20T15:58:43Z", "hours": 8.5708, "text": "added many features like day and night light take a tour and adjust icons sizes to a bit smaller and fixed some backend issues and run many real tests like rag agent and hermes agent and again applied for google search console ——————————————————————————————————— i need to buy a domain but i do not have money i will start marketing after buying domain byee guyss please like"}, {"post_id": 29572, "ts": "2026-07-16T16:32:50Z", "hours": 10.4061, "text": "does a full audit by ai agent found some production bugs working on prd section 1 because it is main it conation python package updtaes because the whole project is up to sdk byee meet in next vlog"}, {"post_id": 27112, "ts": "2026-07-12T11:44:54Z", "hours": 11.8228, "text": "all test are passing"}, {"post_id": 25197, "ts": "2026-07-09T08:46:20Z", "hours": 13.8544, "text": "added new features"}, {"post_id": 20965, "ts": "2026-07-02T18:13:39Z", "hours": 22.3356, "text": "Ahh infinite .tsx bug fixing"}, {"post_id": 12234, "ts": "2026-06-19T23:39:44Z", "hours": 9.5261, "text": "dev vlog#7today i worked on research on my project about similar projects because i cant get a .com domain hi have to buy a .ai domain and i worked on dashboard today because it was so simple then guys please suggest any improvement."}, {"post_id": 9898, "ts": "2026-06-16T11:58:18Z", "hours": 5.4533, "text": "Dev Vlog #6Finally, the landing page work is officially done! The platform is ready for users, and you can check it out here: swarmtrace.vercel.app.Right now, I don’t have the budget to buy a custom domain, so I have set it up on Google Search Console for now. I know that having a domain like swarmtrace.ai would help it perform much better in Google Search, since Google tends to rank subdomains poorly.That being said, the platform is still very much in the building phase. I am already working on adding some exciting new features.Stay tuned, and bye for now!"}, {"post_id": 7404, "ts": "2026-06-12T10:32:32Z", "hours": 8.6239, "text": "finally live on vercel but still in deployment you can check it guys go on swarmtrace.vercel.app"}, {"post_id": 5009, "ts": "2026-06-08T17:33:24Z", "hours": 0.6333, "text": "dev vlog #4 builders just finished the backend work for today / rest work tomorrow i will make the frontend connected to clerk for auth and neon for database . byee👍"}, {"post_id": 4889, "ts": "2026-06-08T13:38:09Z", "hours": 5.7483, "text": "dev vlog 3”What’s up, builders! Today we are fundamentally changing how SwarmTrace works. We’re tearing out the local SQLite restrictions and turning this project into a distributed, multi-tenant AI agent monitoring platform. The goal? Anyone can install our package, drop in an API key, and instantly view their agent’s execution traces on a sleek, hosted Next.js dashboard powered by Clerk and Supabase—without hosting a single thing themselves.” What We’re Solving Today: The “Localhost” Trap: Right now, the FastAPI backend expects to live on the same machine as the SQLite database. If a user runs an agent on their machine, they can’t easily see it on a globally hosted dashboard. Multi-Tenancy: We need to make sure User A never sees User B’s logs. Every single trace must be bound to an organization or user ID. Zero-Infra for the User: The developer using tracely should only have to pip-install it, provide a SWARMTRACE_API_KEY, and let our remote backend handle the rest. 📐 The SaaS Architecture Blueprint Here is how data will flow across the internet from your user’s local python script straight to your Vercel-hosted frontend dashboard: [ User’s Local Machine ] └── Running AI Agent (LangChain/CrewAI/AutoGen) └── Tracely SDK (Decorators) │ ▼ (Async Background HTTP POST /ingest) [ Vercel Serverless / API Gateway ] ─── (Validates SWARMTRACE_API_KEY) │ ▼ (Writes Log Payload) [ Hosted Database: Supabase / Postgres ] └── traces table (Schema: id, user_id, execution_time, trace_payload) ▲ │ (Fetches user-specific traces via Clerk JWT auth) [ Vercel Frontend: Next.js + Clerk ] └── Protected Dashboard View (Only shows rows matching user_id) 🗂️ The Technical Roadmap To pull this off, we have to transform four key elements of your repository: The SDK (tracely/scraper.py & tracer.py) Current: Writes to a local SQLite database via storage.py. SaaS Modification: Introduce a background background-worker thread or async pool. When a trace finishes, it sends an HTTP POST request to https://api.swarmtrace.dev/v1/ingest carrying the payload and an Authorization: Bearer st_live_… header. The Backend Engine (api.py) Current: Reads from local SQLite. SaaS Modification: Drop SQLite. Migrate to a hosted Postgres instance (Supabase is perfect here). Add an /ingest route for incoming agent metrics. Add middleware to validate incoming user requests from the frontend using Clerk JWT tokens. The Authentication Layer (Clerk) Wrap your frontend-next/ layout inside a . Set up a pipeline where a user logs in, generates an “Agent API Key” from their settings dashboard, and pastes that key into their Python .env file. also tracely is maded by me it is the part of swarm trace."}, {"post_id": 3574, "ts": "2026-06-06T15:55:13Z", "hours": 1.6264, "text": "🐝 SwarmTrace DevLog #3 — Wiring the Dashboard to Real Data June 6, 2026 /////////////////////////////////////////////////////// Today was all plumbing. No new features, no design work — just making the two halves of this project actually talk to each other. /////////////////////////////////////////////////////// Where Things Stood I had two things running independently. On one side, the FastAPI server — started it up this morning, it boots clean, the @observe decorator has been collecting real traces into SQLite every time an agent function runs. The data is there. It’s been there. On the other side, the dashboard UI — five pages, looks exactly like the design mockups, dark theme, all the charts and tables and drawers. Finished that yesterday. The problem is they had never spoken to each other. The UI was showing hardcoded numbers I typed in by hand. The API was sitting there with real data and nothing asking for it. The Work Spent today closing that gap. First thing I did was map out what each page actually needs from the backend. Overview needs health stats and an activity feed. Traces needs the full call list. Agents needs a registry of everything that’s been observed. Metrics needs cost and token breakdowns. Settings needs API key management and integration status. Then I went into the API and built those endpoints out one by one. The good news — all the underlying data was already in the database from the decorator. I wasn’t inventing anything, just exposing it in the right shape. The annoying part was that the frontend and backend had grown slightly different vocabularies. The UI expected fields named one way, the API had them named another. Duration in milliseconds vs latency in seconds. function_name vs function. Small things, but they all had to be aligned or every fetch would silently return nothing. CORS was the other thing that caught me — frontend on one port, backend on another, browser refuses to talk between them without the right middleware. Quick fix once I remembered that’s what was happening. The Moment it Clicked Started both servers. Opened the dashboard. Traces page loaded with real function names — execute_task, plan_steps, fetch_data — things I’d actually run this week. Real latencies. Real token counts. Real errors with the actual error messages. Turned on Live Mode. Ran an agent task in a separate terminal. Watched the row appear in the table a couple seconds later. That’s the thing you build toward. What’s Still Hardcoded A few things I haven’t gotten to yet. The Overview stat cards are still static numbers — need to write proper aggregation logic for those. The per-agent token sparklines are placeholder data because I’m storing traces, not time-bucketed token history per agent. Something to fix this week. Tomorrow Get the Overview cards pulling from real DB aggregations. Then figure out deployment — right now this whole thing requires running two local servers which isn’t great for a demo. Want a single public URL before the submission deadline. /////////////////////////////////////////////////////// SwarmTrace — open-source observability for multi-agent AI systems. Built for AMD Hackathon 2026."}, {"post_id": 3416, "ts": "2026-06-06T10:35:54Z", "hours": 3.8981, "text": "🐝 SwarmTrace — Developer Log 1 Project: SwarmTrace Version: 0.1.7 Built for: AMD Hackathon Author: Ravi Kumar What is it? SwarmTrace is a tracing and observability library for LLM-based multi-agent systems — essentially “pytest for AI agents.” The core idea: AI swarms are unpredictable, and existing tools give you no visibility into what happened, where it broke, or whether a prompt change caused a regression. SwarmTrace fixes that. What it does Tracing — wrap any Python function (sync or async) with @observe and every call is automatically recorded: inputs, outputs, latency, token usage, cost, errors, and parent–child relationships between nested agent calls. Token Budget — the @budget decorator tracks cumulative token usage across calls and warns you (or hard-stops) before an agent blows past its limit. Regression Detection — the compare() function runs two prompt versions against the same inputs and uses an LLM to score output similarity, flagging regressions automatically. Tool Attention (ISO Scoring) — implements the technique from arXiv:2604.21816. Instead of injecting all tool schemas into every prompt, it uses FAISS + sentence embeddings to select only the top-k relevant tools per query — reducing tool token overhead by up to 95%. Web Scraping Traces — scraper.scrape(url) wraps Scrapling with full trace recording so scrape calls show up in the call tree like any other agent action. CLI — swarmtrace prints a rich call tree with costs and statuses. swarmtrace-replay replays any individual trace. swarmtrace-export dumps everything to JSON or CSV. Frontend Dashboard A React + TanStack Router frontend visualises live traces from the FastAPI backend: Stat bar — total traces, cumulative tokens, total cost, error count at a glance Call Tree — hierarchical view of parent→child agent calls for a full run Waterfall — timeline view showing which agents ran in parallel vs sequentially Token Chart — per-trace token usage over time Failures page — isolated view of every errored trace with its call chain and error message Detail Drawer — click any trace to inspect its full args, output, latency, and lineage Live Mode — polls the backend every 2 seconds for real-time trace updates Storage All traces land in a local SQLite database (~/.tracely.db) with WAL mode enabled. Auto-purges oldest rows beyond 10,000 to stay bounded. Indexed on timestamp for fast ordered reads. Export to JSON/CSV available via CLI. Stack LayerTechnologyCore libraryPython 3.8+, SQLite, contextvarsRegression LLMClaude Haiku via litaiTool selectionFAISS + sentence-transformersAPIFastAPI + PydanticFrontendReact, TanStack Router/Start, Tailwind, RechartsCLI displayrichPackagingsetuptools, pip-installable Key design decisions contextvars for parent tracking — parent IDs propagate automatically through async and threaded code without any manual wiring. Nested @observe calls build the tree on their own. SQLite over a hosted DB — zero setup for the user; works offline; the auto-purge keeps it from growing unboundedly. Graceful degradation everywhere — if sentence-transformers/FAISS aren’t installed, Tool Attention falls back to returning the first k tools. If tiktoken isn’t installed, budget tracking falls back to len // 4. Storage errors never crash the agent being traced. Current limitations Failures page still uses static demo data instead of the live API swarm_module_0–4.py are five identical placeholder files (to be collapsed into one) No authentication on the API (development only) pyproject.toml is incomplete; setup.py not yet migrated. i have to complete ui first now then i will do other api work because it will be connected to ui because it was originally built a python frame work.byee"}]...
|
Title: swarmtrace by @raviiiibro | Stardance
Devl Title: swarmtrace by @raviiiibro | Stardance
Devlogs:
🐝 SwarmTrace — Developer Log 1 Project: SwarmTrace Version: 0.1.7 Built for: AMD Hackathon Author: Ravi Kumar What is it? SwarmTrace is a tracing and observability library for LLM-based multi-agent systems — essentially “pytest for AI agents.” The core idea: AI swarms are unpredictable, and existing tools give you no visibility into what happened, where it broke, or whether a prompt change caused a regression. SwarmTrace fixes that. What it does Tracing — wrap any Python function (sync or async) with @observe and every call is automatically recorded: inputs, outputs, latency, token usage, cost, errors, and parent–child relationships between nested agent calls. Token Budget — the @budget decorator tracks cumulative token usage across calls and warns you (or hard-stops) before an agent blows past its limit. Regression Detection — the compare() function runs two prompt versions against the same inputs and uses an LLM to score output similarity, flagging regressions automatically. Tool Attention (ISO Scoring) — implements the technique from arXiv:2604.21816. Instead of injecting all tool schemas into every prompt, it uses FAISS + sentence embeddings to select only the top-k relevant tools per query — reducing tool token overhead by up to 95%. Web Scraping Traces — scraper.scrape(url) wraps Scrapling with full trace recording so scrape calls show up in the call tree like any other agent action. CLI — swarmtrace prints a rich call tree with costs and statuses. swarmtrace-replay replays any individual trace. swarmtrace-export dumps everything to JSON or CSV. Frontend Dashboard A React + TanStack Router frontend visualises live traces from the FastAPI backend: Stat bar — total traces, cumulative tokens, total cost, error count at a glance Call Tree — hierarchical view of parent→child agent calls for a full run Waterfall — timeline view showing which agents ran in parallel vs sequentially Token Chart — per-trace token usage over time Failures page — isolated view of every errored trace with its call chain and error message Detail Drawer — click any trace to inspect its full args, output, latency, and lineage Live Mode — polls the backend every 2 seconds for real-time trace updates Storage All traces land in a local SQLite database (~/.tracely.db) with WAL mode enabled. Auto-purges oldest rows beyond 10,000 to stay bounded. Indexed on timestamp for fast ordered reads. Export to JSON/CSV available via CLI. Stack LayerTechnologyCore libraryPython 3.8+, SQLite, contextvarsRegression LLMClaude Haiku via litaiTool selectionFAISS + sentence-transformersAPIFastAPI + PydanticFrontendReact, TanStack Router/Start, Tailwind, RechartsCLI displayrichPackagingsetuptools, pip-installable Key design decisions contextvars for parent tracking — parent IDs propagate automatically through async and threaded code without any manual wiring. Nested @observe calls build the tree on their own. SQLite over a hosted DB — zero setup for the user; works offline; the auto-purge keeps it from growing unboundedly. Graceful degradation everywhere — if sentence-transformers/FAISS aren’t installed, Tool Attention falls back to returning the first k tools. If tiktoken isn’t installed, budget tracking falls back to len // 4. Storage errors never crash the agent being traced. Current limitations Failures page still uses static demo data instead of the live API swarm_module_0–4.py are five identical placeholder files (to be collapsed into one) No authentication on the API (development only) pyproject.toml is incomplete; setup.py not yet migrated. i have to complete ui first now then i will do other api work because it will be connected to ui because it was originally built a python frame work.byee
🐝 SwarmTrace DevLog #3 — Wiring the Dashboard to Real Data June 6, 2026 /////////////////////////////////////////////////////// Today was all plumbing. No new features, no design work — just making the two halves of this project actually talk to each other. /////////////////////////////////////////////////////// Where Things Stood I had two things running independently. On one side, the FastAPI server — started it up this morning, it boots clean, the @observe decorator has been collecting real traces into SQLite every time an agent function runs. The data is there. It’s been there. On the other side, the dashboard UI — five pages, looks exactly like the design mockups, dark theme, all the charts and tables and drawers. Finished that yesterday. The problem is they had never spoken to each other. The UI was showing hardcoded numbers I typed in by hand. The API was sitting there with real data and nothing asking for it. The Work Spent today closing that gap. First thing I did was map out what each page actually needs from the backend. Overview needs health stats and an activity feed. Traces needs the full call list. Agents needs a registry of everything that’s been observed. Metrics needs cost and token breakdowns. Settings needs API key management and integration status. Then I went into the API and built those endpoints out one by one. The good news — all the underlying data was already in the database from the decorator. I wasn’t inventing anything, just exposing it in the right shape. The annoying part was that the frontend and backend had grown slightly different vocabularies. The UI expected fields named one way, the API had them named another. Duration in milliseconds vs latency in seconds. function_name vs function. Small things, but they all had to be aligned or every fetch would silently return nothing. CORS was the other thing that caught me — frontend on one port, backend on another, browser refuses to talk between them without the right middleware. Quick fix once I remembered that’s what was happening. The Moment it Clicked Started both servers. Opened the dashboard. Traces page loaded with real function names — execute_task, plan_steps, fetch_data — things I’d actually run this week. Real latencies. Real token counts. Real errors with the actual error messages. Turned on Live Mode. Ran an agent task in a separate terminal. Watched the row appear in the table a couple seconds later. That’s the thing you build toward. What’s Still Hardcoded A few things I haven’t gotten to yet. The Overview stat cards are still static numbers — need to write proper aggregation logic for those. The per-agent token sparklines are placeholder data because I’m storing traces, not time-bucketed token history per agent. Something to fix this week. Tomorrow Get the Overview cards pulling from real DB aggregations. Then figure out deployment — right now this whole thing requires running two local servers which isn’t great for a demo. Want a single public URL before the submission deadline. /////////////////////////////////////////////////////// SwarmTrace — open-source observability for multi-agent AI systems. Built for AMD Hackathon 2026.
dev vlog 3”What’s up, builders! Today we are fundamentally changing how SwarmTrace works. We’re tearing out the local SQLite restrictions and turning this project into a distributed, multi-tenant AI agent monitoring platform. The goal? Anyone can install our package, drop in an API key, and instantly view their agent’s execution traces on a sleek, hosted Next.js dashboard powered by Clerk and Supabase—without hosting a single thing themselves.” What We’re Solving Today: The “Localhost” Trap: Right now, the FastAPI backend expects to live on the same machine as the SQLite database. If a user runs an agent on their machine, they can’t easily see it on a globally hosted dashboard. Multi-Tenancy: We need to make sure User A never sees User B’s logs. Every single trace must be bound to an organization or user ID. Zero-Infra for the User: The developer using tracely should only have to pip-install it, provide a SWARMTRACE_API_KEY, and let our remote backend handle the rest. 📐 The SaaS Architecture Blueprint Here is how data will flow across the internet from your user’s local python script straight to your Vercel-hosted frontend dashboard: [ User’s Local Machine ] └── Running AI Agent (LangChain/CrewAI/AutoGen) └── Tracely SDK (Decorators) │ ▼ (Async Background HTTP POST /ingest) [ Vercel Serverless / API Gateway ] ─── (Validates SWARMTRACE_API_KEY) │ ▼ (Writes Log Payload) [ Hosted Database: Supabase / Postgres ] └── traces table (Schema: id, user_id, execution_time, trace_payload) ▲ │ (Fetches user-specific traces via Clerk JWT auth) [ Vercel Frontend: Next.js + Clerk ] └── Protected Dashboard View (Only shows rows matching user_id) 🗂️ The Technical Roadmap To pull this off, we have to transform four key elements of your repository: The SDK (tracely/scraper.py & tracer.py) Current: Writes to a local SQLite database via storage.py. SaaS Modification: Introduce a background background-worker thread or async pool. When a trace finishes, it sends an HTTP POST request to https://api.swarmtrace.dev/v1/ingest carrying the payload and an Authorization: Bearer st_live_… header. The Backend Engine (api.py) Current: Reads from local SQLite. SaaS Modification: Drop SQLite. Migrate to a hosted Postgres instance (Supabase is perfect here). Add an /ingest route for incoming agent metrics. Add middleware to validate incoming user requests from the frontend using Clerk JWT tokens. The Authentication Layer (Clerk) Wrap your frontend-next/ layout inside a . Set up a pipeline where a user logs in, generates an “Agent API Key” from their settings dashboard, and pastes that key into their Python .env file. also tracely is maded by me it is the part of swarm trace.
dev vlog #4 builders just finished the backend work for today / rest work tomorrow i will make the frontend connected to clerk for auth and neon for database . byee👍
finally live on vercel but still in deployment you can check it guys go on swarmtrace.vercel.app
Dev Vlog #6Finally, the landing page work is officially done! The platform is ready for users, and you can check it out here: swarmtrace.vercel.app.Right now, I don’t have the budget to buy a custom domain, so I have set it up on Google Search Console for now. I know that having a domain like swarmtrace.ai would help it perform much better in Google Search, since Google tends to rank subdomains poorly.That being said, the platform is still very much in the building phase. I am already working on adding some exciting new features.Stay tuned, and bye for now!
dev vlog#7today i worked on research on my project about similar projects because i cant get a .com domain hi have to buy a .ai domain and i worked on dashboard today because it was so simple then guys please suggest any improvement.
Ahh infinite .tsx bug fixing
added new features
all test are passing
does a full audit by ai agent found some production bugs working on prd section 1 because it is main it conation python package updtaes because the whole project is up to sdk byee meet in next vlog
added many features like day and night light take a tour and adjust icons sizes to a bit smaller and fixed some backend issues and run many real tests like rag agent and hermes agent and again applied for google search console ——————————————————————————————————— i need to buy a domain but i do not have money i will start marketing after buying domain byee guyss please like
adding agent graph this is mvp only i will polish it byee
today i know there is so much work left on this project then i think i added a floating side bar added some more now features for backend optimization guys working on a i3 sucks please help me to buy an macbook byyy
starting work
half work done
Ships:
the marketing was challenging and i think now it is ready and i get more suggestion if people will use it and i hope i ccan buy a domain by the star dust...
|
1792835482.262652
|
1792921882.262652
|
|
7968
|
Chess Now! by @AngelCMHxD | Stardance
|
AngelCMHxD
|
1
|
140.11
|
1509
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-07-30T15:00:21Z", "hours": 137.0, "multiplier": 18.82, "payout": 1509, "blessing": "neutral", "text": "This is Chess Now! A real-time chess API with challenges handling, matches, friends, and much more! The idea is for anyone to be able to make a custom client, bot or integration with the platform, thus it’s as open as it can possibly be while trying to also stay secure. One of the biggest parts of the proyect was doing that many clients, especially the TUI client. While I learned Vue doing the TUI client, I found it’s behaviour kinda… unintuitive in some way I can’t really explain. Though at the end I felt that Vue is better than React by a mile (now I only have to try out Svelte) I am proud of how well structured the API is, and while I’m sure it can be improved, the bases feel pretty solid. I’m also proud of how much I learned doing this. I mean, I tried a lot of things I’ve been meaning to try for some time already: Vue, Better Auth, Drizzle ORM, actually deploying a Next.js app, React mail + Resend, Elysia, and Fumadocs. And probably something else I already forgot about hehe Would I do something different if I started right now? Probably, but mostly just to try out even more things, which is the main reason why I do most of my projects. If you want to try out the project, you should probably just check the instance hosted at https://chessnow.angelcmh.com/ , create an account and play against a bot. You should also check out the docs, or the npm package over at https://www.npmjs.com/package/@chess-now/api if you are interested. Thanks for reading till the end, have a great day! ;P", "repo": "https://github.com/AngelCMHxD/chess-now", "demo": "https://chessnow.angelcmh.com/"}]...
|
[{"post_id": 62578, "ts": "202 [{"post_id": 62578, "ts": "2026-09-06T01:20:34Z", "hours": 3.1125, "text": "aaaa, so… long time no see! also, super star!!! So, I’ve began doing some things that I’ve meant to do before, but completely forgot as I got quite busy… lol I fixed an error which made the TUI not work on Linux, it works now! It was an issue as I was building on NixOS, and the linker path was on my /nix/store folder instead of the usual /lib one. that means it also broke for me after an update lol You can now configure challenges! This needed to be implemented on all clients, hence why it took a little while lol Select whether you want to start with white, black or random. The opponent will be able to see which color you selected before accepting the challenge though! :P Also, meet unrankeds! You can now also mark a match as unranked so it doesn’t affect your ELO ratings at all . Take into account that there is decay though! so if you don’t play ranked for a while, your next match ELO diffs might be a little bit different Fun fact: The starting color option has been available on the API for a while already, even documented. so, if you did API calls directly, you could have selected a starting color and the opponent would not have been able to notice! as none of the clients showed it. except for the Discord bot… I think… or if you requested the match through the API too ;D"}, {"post_id": 37641, "ts": "2026-07-30T14:59:49Z", "hours": 0.8269, "text": "Hewo! This devlog was unexpected. I prepared a small video/trailer so I had something to upload when shipping the project, and when I tried to upload it I realized that you can only upload images when shipping a project :/ So here is that video as it won’t go to waste. I tried my best, hope my voice doesn’t sound awkward, I’m not a native speaker Also, check out the shipped project! As I probably shipped it right after this devlog"}, {"post_id": 37305, "ts": "2026-07-29T22:23:23Z", "hours": 3.36, "text": "Well, I set up the domain + reverse proxy and all kind of problems arose. I changed the package so you only need to instantiate the class without any arguments to use the default server over at https://chessnow.angelcmh.com though you can still pass an optional baseUrl in case you selfhost your own instance at first nothing worked, but then i noticed that I had a port conflict on my VPS with two things trying to use the same port lol i also tried to improve SEO on the main page as much as I could, though im not an expert on that. i also added an official bots section so you can challenge some bots without having to look at any docs to find them shipping in some minutes!"}, {"post_id": 36858, "ts": "2026-07-29T05:39:21Z", "hours": 6.6392, "text": "Hewo! Last devlog till ship here (i think) I didn’t think I’d add anything else, but you can now forfeit matches or ask the opponent for a draw! They just seemed like something that should exist on the shipped project… Each player can request only one draw per match, so you think before you do it. why did i do it like that? well, asking for a draw pauses the whole match, an neither player can do any moves until the draw request is answered. in order to prevent one of the users spamming draws, i simply limited to only one per match. also, ending the match in a draw doesn’t mean that your ELO isn’t affected, it is affected. depending on the elo of your opponent, you’ll lose/earn elo on a draw, just as you’d do on a stalemate. I also added a piece promotion selector. i forgot i made it so it always chose the queen by default, but in some matches that might not be wanted, so now you get a popup to select the piece you want to promote to well, i don’t really know if i’m missing any other things, as that was all that was i had on my TODO list, so i guess i’ll start setting up the prod environment below is a video showing the forfeit and draw flows, along with the promotion popup. don’t worry about the first match being a valid checkmate but the match continued being active, i already fixed that bug but it didn’t apply to already affected matches :P i also sped up (is that the correct way to say it?) and cutted out some parts of the video to prevent it from being 6 minutes of me playing the match lol"}, {"post_id": 34658, "ts": "2026-07-25T15:44:56Z", "hours": 9.5286, "text": "Well, that took way longer than expected. The docs are way more polished now! All endpoints have example responses so you know exactly what each one will return, along with a small description. HTTP endpoints that trigger a WebSocket event for any user (like friend requests, challenges, match moves, etc), mention it on their descriptions. I also added some headers to the PGN strings, as they were previously filled with “?“s. This mostly took this long due to a LOT of issues with the account deletion process, but it works perfectly now. When you delete your account, your user is replaced in all the matches you’ve played to @deleted_user . This is to keep the match data available even after any user deletes it. After that, your account is deleted from the DB. Due to how the DB is configured with cascade deletes, that will delete all other rows that reference the user (friendships, friend requests, pending challenges, etc.) except for matches, as they no longer reference the user that is being deleted. I’m am quite close to the ship, which I’m kinda nervous about. I want the project to be in the best state and as polished as possible, without any bugs (well, any known bugs lol). Which is mostly the reason this project as a whole has taken soo long :/ Below is a small video showing the docs with the example data. At the end is the delete account flow, which have lot’s of confirmations as it currently doesn’t send an email. I’m thinking it probably should send one lol See you in the next (and probably last) devlog! (I fixed my OBS settings, the video should look a bit better than the previous ones :P)"}, {"post_id": 33878, "ts": "2026-07-24T04:38:40Z", "hours": 5.6958, "text": "ELO system! let’s go!! At the end, I did add an ELO system. Well, it’s not really ELO, it’s Glicko . I used the glicko2-lite library, which is stateless unlike other glicko implementations I found. I wanted a stateless one because I’m saving the data on the DB anyways, so making it stateful would only make it use more resources (primarly RAM). You start with a rating of 1500. After every match your rating will go up/down depending on whether you win or lose. How much you win or lose depends on how many matches you have played (the first one are worth a bit more to get you to the optimal points), and depending on the rating of your oponent. If you win against someone with a bigger rating than yours, you’ll gain way more points than if you win agains someone around your rating or less. I also added a settings page so you can change your name and username, along with a page to change your password. Below is a small video showing how the ELO Glicko system works: What remains is just to also add the name/username change for bots and improve the docs. I also made a Stockfish bot which you can try to play against. (Keyword: try , because it’s probably almost impossible)"}, {"post_id": 33281, "ts": "2026-07-23T02:11:49Z", "hours": 2.855, "text": "User page done! You can now see all of the matches and friends of other people, along with a small list of stats. I’m thinking about implementing the ELO system. While I previously said that I wouldn’t be able to moderate people artificially inflating it (by selfbotting), having it without a leaderboard wouldn’t really make it that enticing to abuse. And personally, if there isn’t a leaderboard, I don’t think moderating it would be that important, what would the user gain more than having a bigger number on their profile? Still thinking about it though I’m not sure about adding custom profile pictures or really any other user generated content. It would be nice to have, but it also involves moderation in order to prevent anything inappropriate from being uploaded (i mean, we all know how the internet works :/) Below is a small video showing the user page, along with the bot auto accepting any new friend requests :P"}, {"post_id": 32786, "ts": "2026-07-22T05:22:14Z", "hours": 6.9678, "text": "Well, the web client is done!! I used react-chessboard and it’s a pretty good library. I didn’t add click-to-move pieces as it makes the logic a LOT more complex, so I didn’t want to do it for now I also used the API package from before, so we can use the websocket connection and listen for any changes. I also added it onto the matches page so any updates on the boards are reflected in real-time (which would probably look amazing if you have multiple automated matches running lol), and any new matches are added to the list without needing to refresh the page. I also want to add it onto the friends page, so it also updates in real-time I had to change the icons on the discord bot as I realized they were Chess.com’s icons, and they aren’t free to use, so I removed them and replaced them with a pack named Caliente , which look amazing. It’s the same one I use on the web client I think I should add a popup once the match is over as the current result feels quite plain. I also gotta add another page where you can spectate the match of other people, as the current one has too many logic already I prefer to duplicate it and remove the need of handling input and what not. I also haven’t added a profiles page yet, so that’s still on the TODO list hahah Below is a small video of me playing a match against a bot. Two disclaimers: I used AI to fix some issues I had with the CSS on the page, though it didn’t change anything other than fix some class names, and the whole logic was made by me. I played horribly against the bot, as the difficulty is set quite low lol Additional disclaimer: Ignore the -55s duration on one of the matches, it was an old bug but I’ve gotten lazy and haven’t fixed the match on the DB yet .-."}, {"post_id": 32426, "ts": "2026-07-21T16:34:43Z", "hours": 0.685, "text": "The TUI is 100% done! (I think…) There is no longer any mock data and it works with the actual API. You can send and receive challenges and friend requests, and you can see/play your running matches. I think that, for the account management, I’ll remove email signups and only allow social OAuth2 (currently Google and Discord). As with email/password signups I need to handle email verification and password reset, and while Resend (what I use right now) is REALLY good and it’s free tier is great (3000 mails a month, 100 daily), if someone tries to spam the endpoints, it’s gonna eat up the limit. Though I might just put a rate limiter in place, and with Cloudflare Turnstile I already combat at least some botting, still thinking about it… I’ll probably start with the web client for now, which might be the last thing to do Below is a little video showing the TUI in action, while playing a match: (I took quite a bit of time thinking about the move I should make till I remembered I was just recording a test video lol)"}, {"post_id": 32210, "ts": "2026-07-21T07:10:56Z", "hours": 0.5875, "text": "And… This devlog is just to show the web dashboard, which I have forgotten to do a devlog for it all this time, which is why the logged time is so low lol I used the Next.js framework to build the web dashboard (well, the whole dashboard + welcome page and just the whole web thing). I also used the shadcn/ui components library, as I’m not that good making frontend stuff, so that helped a lot. The dashboard layout is actually a template you can find on the shadcn site, I love the huge amount of things they have I didn’t know what else to put as a welcome to the dashboard, so I left a little welcome message as seen at the beginning of the video And I also forgot to show (mostly because there wasn’t any) the notifications button, where you can find any incoming challenges and friend requests. The whole bot creation process works, at the end it gives you a token that you can use as if it were a user auth token, but you have all the scopes by default, so you can do anything you want with the account What remains to do on this web part is to be able to see the profile of other users, along with their recent matches. And also a web client so you can play without downloading the TUI client or needing to use the discord bot"}, {"post_id": 32193, "ts": "2026-07-21T06:11:10Z", "hours": 5.3728, "text": "Edit: I realized right after posting this devlog: 100 hours! This has taken me more time than what I expected and gone quite big at this point hahah Welp, the bots are done, and now I’ve tested the Discord integration. I made the bots using the js-chess-engine package. Why that instead of something like stockfish? it simply was way easier to do as it’s a full js/ts engine lol Though, that doesn’t mean that the stockfish engine wouldn’t be posible to integrate! as anyone can create bots, that also means that anyone could create a stockfish bot if they wanted to. In case you don’t know what Stockfish is: It’s a really powerful and popular chess engine written in C++. Also, while writing this devlog, I found this package , which could be used to make a stockfish bot really easily. Though the ELO of the stockfish bot is really high, so I don’t really regret choosing js-chess-engine as a simpler engine means people can at least win against it lol Here is a small video showing a match running on the discord bot. There are some small coordinates on the image so writing the SAN/LAN notation is easier, though it’s quire hard to see them on the video due to compression. While it has support for everything (friend requests, send challenges, accept challenges, etc), I kinda forgot to record the rest :P"}, {"post_id": 31519, "ts": "2026-07-20T00:11:39Z", "hours": 9.0231, "text": "Well, making the TUI has gone quite well. While I initially thought that Vue seemed weird for doing TUIs, it’s actually not that bad. No wonder Ink is so popular. (Though I’m liking Vue more than I like React, and Ink felt kinda clunky at times when I first tried it) If you want to do TUIs with TypeScript/JavaScript, I’d recommend you give Vue TermUI a try. It also supports the Vue Router, so it’s easy to make a lot of pages and then navigate through them. The data show in the video is just mock data. So it still doesn’t use the actual API, though progress have gone quite well without many hiccups. The send friend request button, and the accept/reject challenge buttons do use the actual API, but they just throw an error because it tries to accept/reject a challenge that doesn’t actually exist, as it’s just mock data. The app doesn’t crash on error though, it displays the error message right below the button, that way the user can see what went wrong without having the whole app crash or any other unexpected behavior. What remains now is making some bots, that way I can make the whole “Playing a game” page, as having mock data for the WHOLE game state is not very… easy? I mean, I’ll have to do some bots anyway, so why not do them now instead of creating some weird mock functions hahah I also think that the icons on the authorization page feel weird. I dont know if maybe they are too big or if I should just change them to other ones. I also have to change the card after approval or denial instead of just showing the toast at the bottom, as it doesnt give feedback to the user Below is a small video of the TUI in action: (Disclaimer: Some AI was used, primarily to create the mock data so I could focus on the TUI itself)"}, {"post_id": 30506, "ts": "2026-07-18T04:16:32Z", "hours": 8.6408, "text": "Welp, designing how to do the TUI was kinda… complex. I mostly know JS/TS, so I wanted to use that. And that made everything way more complex, as there aren’t so many options as you might have with something like how good Bubble Tea is on Go. So these were the options I considered: Ink : Which didn’t have mouse support and uses React, which I’ve felt kinda clunky while doing the webpage, so I wanted try something else. Rezi : Which, at first, was AMAZING. But it had some bugs where focus didn’t work at all and I couldn’t interact with the TUI, or just started getting weird. OpenTUI : tbf, I didn’t test it out that much, couldn’t really understand it’s TS bindings So what I ended up going with? Something not listed above: I ended up going with Vue TermUI ( for now ). Why? Well, I’ve wanted to try out Vue for some time now, and it uses OpenTUI under the hood, so it’s backend is probably pretty stable, or so I think, i don’t really know. It does handles mouse input and focus management, making it quite easy to use. Will I stay with Vue TermUI? I don’t really know yet. I’m just beginning to try it out, and I’m curious to see how it ends up. Especially because Vue for TUI sounds pretty weird lol I actually really liked Rezi and you should check it out, as the issues I had were probably because I was doing something wrong hahah"}, {"post_id": 24535, "ts": "2026-07-08T04:57:03Z", "hours": 62.6878, "text": "Welp, I’ve been working for quite a while and this is quite a long devlog. If this seems interesting to you, leave a comment with suggestions! Maybe I could do some of them depending on their difficulty I now have a pretty good API structure. It has normal HTTP endpoints and also a subscription system using WebSockets so you can get event-based messages, all of it working already. I also did a JS/TS package so it’s easier to implement it on your proyects. (Still not published to npm) I currently have the following systems working: Account, Friends, Challenges, Matches, Bot management and Device auth (the thing where the app can show you an 8 character code to login). I also did a scopes system, that way you can know exactly what data the app will have access to and what actions it’d be able to do on your behalf I made the backend using Elysia , while the whole storage is a PostgreSQL database that I interface with using Drizzle . The doc pages were done using Fumadocs , I love the way they look :D A secondary DB, using Redis (or Redis-compatible DB) is also optional, quite useful as some thing like board movements might be done by players quite fast on low timers (like 1 min) and should be replied to as fast as possible to prevent the DB write speeds and reply time from counting against a player’s timer or affecting the match playability, though I still haven’t implemented timers lol If the secondary Redis DB isn’t setup, then that data will be stored on RAM in order to keep it fast. Things like match status is kept there until the match ends, that means if the API server restarts, the matches that are currently being played are restarted too. I thought this isn’t as bad of a tradeoff in order to have a fast API reply, especially because you can solve it by setting up the Redis DB, and then you can restart the API server without worry, as the data is stored on the Redis server. While the whole API is done, the account dashboard still requires some work to do, and I’m planning to do some integrations to make it easy to test, like a Discord bot (which is already done), a TUI, or maybe a web interface, though frontend interactivity isn’t something I’m really good at, and still haven’t gotten used to doing things “the React way” :P I also added some bot management endpoints. That way someone can create a bot account under their user and be able to create any “bot opponent” to play against, something that I also want to do so anyone trying out the API or implementations can test it without requiring another available opponent Don’t know if I should make an ELO/Leaderboard system, as those could be easily cheated and can’t really moderate them, and doing skill based match making wouldn’t work with a small user base."}, {"post_id": 7750, "ts": "2026-06-12T22:58:59Z", "hours": 14.0508, "text": "This is Chess Now! An open-source (soon!) API to control chess games. The idea is for anyone to be able to create and integrate their apps and be able to control the games from there. This also means that you could be playing on App1 while your opponent can be playing on App2, as the server-side is shared between them. I’ve added the whole auth flow and database integration. Better Auth really helped a lot in this regard, the docs were awesome. I used some templates for the hero and login/signup flow as I don’t have almost any experience with frontend development (and a side-goal for me is to not use AI for codegen while making this project), though I’ve been learning a LOT about frontend doing this. I’ll begin planning the whole API structure, I still don’t know where to begin hahah"}]...
|
Title: Chess Now! by @AngelCMHxD | Stardance
Devl Title: Chess Now! by @AngelCMHxD | Stardance
Devlogs:
This is Chess Now! An open-source (soon!) API to control chess games. The idea is for anyone to be able to create and integrate their apps and be able to control the games from there. This also means that you could be playing on App1 while your opponent can be playing on App2, as the server-side is shared between them. I’ve added the whole auth flow and database integration. Better Auth really helped a lot in this regard, the docs were awesome. I used some templates for the hero and login/signup flow as I don’t have almost any experience with frontend development (and a side-goal for me is to not use AI for codegen while making this project), though I’ve been learning a LOT about frontend doing this. I’ll begin planning the whole API structure, I still don’t know where to begin hahah
Welp, I’ve been working for quite a while and this is quite a long devlog. If this seems interesting to you, leave a comment with suggestions! Maybe I could do some of them depending on their difficulty I now have a pretty good API structure. It has normal HTTP endpoints and also a subscription system using WebSockets so you can get event-based messages, all of it working already. I also did a JS/TS package so it’s easier to implement it on your proyects. (Still not published to npm) I currently have the following systems working: Account, Friends, Challenges, Matches, Bot management and Device auth (the thing where the app can show you an 8 character code to login). I also did a scopes system, that way you can know exactly what data the app will have access to and what actions it’d be able to do on your behalf I made the backend using Elysia , while the whole storage is a PostgreSQL database that I interface with using Drizzle . The doc pages were done using Fumadocs , I love the way they look :D A secondary DB, using Redis (or Redis-compatible DB) is also optional, quite useful as some thing like board movements might be done by players quite fast on low timers (like 1 min) and should be replied to as fast as possible to prevent the DB write speeds and reply time from counting against a player’s timer or affecting the match playability, though I still haven’t implemented timers lol If the secondary Redis DB isn’t setup, then that data will be stored on RAM in order to keep it fast. Things like match status is kept there until the match ends, that means if the API server restarts, the matches that are currently being played are restarted too. I thought this isn’t as bad of a tradeoff in order to have a fast API reply, especially because you can solve it by setting up the Redis DB, and then you can restart the API server without worry, as the data is stored on the Redis server. While the whole API is done, the account dashboard still requires some work to do, and I’m planning to do some integrations to make it easy to test, like a Discord bot (which is already done), a TUI, or maybe a web interface, though frontend interactivity isn’t something I’m really good at, and still haven’t gotten used to doing things “the React way” :P I also added some bot management endpoints. That way someone can create a bot account under their user and be able to create any “bot opponent” to play against, something that I also want to do so anyone trying out the API or implementations can test it without requiring another available opponent Don’t know if I should make an ELO/Leaderboard system, as those could be easily cheated and can’t really moderate them, and doing skill based match making wouldn’t work with a small user base.
Welp, designing how to do the TUI was kinda… complex. I mostly know JS/TS, so I wanted to use that. And that made everything way more complex, as there aren’t so many options as you might have with something like how good Bubble Tea is on Go. So these were the options I considered: Ink : Which didn’t have mouse support and uses React, which I’ve felt kinda clunky while doing the webpage, so I wanted try something else. Rezi : Which, at first, was AMAZING. But it had some bugs where focus didn’t work at all and I couldn’t interact with the TUI, or just started getting weird. OpenTUI : tbf, I didn’t test it out that much, couldn’t really understand it’s TS bindings So what I ended up going with? Something not listed above: I ended up going with Vue TermUI ( for now ). Why? Well, I’ve wanted to try out Vue for some time now, and it uses OpenTUI under the hood, so it’s backend is probably pretty stable, or so I think, i don’t really know. It does handles mouse input and focus management, making it quite easy to use. Will I stay with Vue TermUI? I don’t really know yet. I’m just beginning to try it out, and I’m curious to see how it ends up. Especially because Vue for TUI sounds pretty weird lol I actually really liked Rezi and you should check it out, as the issues I had were probably because I was doing something wrong hahah
Well, making the TUI has gone quite well. While I initially thought that Vue seemed weird for doing TUIs, it’s actually not that bad. No wonder Ink is so popular. (Though I’m liking Vue more than I like React, and Ink felt kinda clunky at times when I first tried it) If you want to do TUIs with TypeScript/JavaScript, I’d recommend you give Vue TermUI a try. It also supports the Vue Router, so it’s easy to make a lot of pages and then navigate through them. The data show in the video is just mock data. So it still doesn’t use the actual API, though progress have gone quite well without many hiccups. The send friend request button, and the accept/reject challenge buttons do use the actual API, but they just throw an error because it tries to accept/reject a challenge that doesn’t actually exist, as it’s just mock data. The app doesn’t crash on error though, it displays the error message right below the button, that way the user can see what went wrong without having the whole app crash or any other unexpected behavior. What remains now is making some bots, that way I can make the whole “Playing a game” page, as having mock data for the WHOLE game state is not very… easy? I mean, I’ll have to do some bots anyway, so why not do them now instead of creating some weird mock functions hahah I also think that the icons on the authorization page feel weird. I dont know if maybe they are too big or if I should just change them to other ones. I also have to change the card after approval or denial instead of just showing the toast at the bottom, as it doesnt give feedback to the user Below is a small video of the TUI in action: (Disclaimer: Some AI was used, primarily to create the mock data so I could focus on the TUI itself)
Edit: I realized right after posting this devlog: 100 hours! This has taken me more time than what I expected and gone quite big at this point hahah Welp, the bots are done, and now I’ve tested the Discord integration. I made the bots using the js-chess-engine package. Why that instead of something like stockfish? it simply was way easier to do as it’s a full js/ts engine lol Though, that doesn’t mean that the stockfish engine wouldn’t be posible to integrate! as anyone can create bots, that also means that anyone could create a stockfish bot if they wanted to. In case you don’t know what Stockfish is: It’s a really powerful and popular chess engine written in C++. Also, while writing this devlog, I found this package , which could be used to make a stockfish bot really easily. Though the ELO of the stockfish bot is really high, so I don’t really regret choosing js-chess-engine as a simpler engine means people can at least win against it lol Here is a small video showing a match running on the discord bot. There are some small coordinates on the image so writing the SAN/LAN notation is easier, though it’s quire hard to see them on the video due to compression. While it has support for everything (friend requests, send challenges, accept challenges, etc), I kinda forgot to record the rest :P
And… This devlog is just to show the web dashboard, which I have forgotten to do a devlog for it all this time, which is why the logged time is so low lol I used the Next.js framework to build the web dashboard (well, the whole dashboard + welcome page and just the whole web thing). I also used the shadcn/ui components library, as I’m not that good making frontend stuff, so that helped a lot. The dashboard layout is actually a template you can find on the shadcn site, I love the huge amount of things they have I didn’t know what else to put as a welcome to the dashboard, so I left a little welcome message as seen at the beginning of the video And I also forgot to show (mostly because there wasn’t any) the notifications button, where you can find any incoming challenges and friend requests. The whole bot creation process works, at the end it gives you a token that you can use as if it were a user auth token, but you have all the scopes by default, so you can do anything you want with the account What remains to do on this web part is to be able to see the profile of other users, along with their recent matches. And also a web client so you can play without downloading the TUI client or needing to use the discord bot
The TUI is 100% done! (I think…) There is no longer any mock data and it works with the actual API. You can send and receive challenges and friend requests, and you can see/play your running matches. I think that, for the account management, I’ll remove email signups and only allow social OAuth2 (currently Google and Discord). As with email/password signups I need to handle email verification and password reset, and while Resend (what I use right now) is REALLY good and it’s free tier is great (3000 mails a month, 100 daily), if someone tries to spam the endpoints, it’s gonna eat up the limit. Though I might just put a rate limiter in place, and with Cloudflare Turnstile I already combat at least some botting, still thinking about it… I’ll probably start with the web client for now, which might be the last thing to do Below is a little video showing the TUI in action, while playing a match: (I took quite a bit of time thinking about the move I should make till I remembered I was just recording a test video lol)
Well, the web client is done!! I used react-chessboard and it’s a pretty good library. I didn’t add click-to-move pieces as it makes the logic a LOT more complex, so I didn’t want to do it for now I also used the API package from before, so we can use the websocket connection and listen for any changes. I also added it onto the matches page so any updates on the boards are reflected in real-time (which would probably look amazing if you have multiple automated matches running lol), and any new matches are added to the list without needing to refresh the page. I also want to add it onto the friends page, so it also updates in real-time I had to change the icons on the discord bot as I realized they were Chess.com’s icons, and they aren’t free to use, so I removed them and replaced them with a pack named Caliente , which look amazing. It’s the same one I use on the web client I think I should add a popup once the match is over as the current result feels quite plain. I also gotta add another page where you can spectate the match of other people, as the current one has too many logic already I prefer to duplicate it and remove the need of handling input and what not. I also haven’t added a profiles page yet, so that’s still on the TODO list hahah Below is a small video of me playing a match against a bot. Two disclaimers: I used AI to fix some issues I had with the CSS on the page, though it didn’t change anything other than fix some class names, and the whole logic was made by me. I played horribly against the bot, as the difficulty is set quite low lol Additional disclaimer: Ignore the -55s duration on one of the matches, it was an old bug but I’ve gotten lazy and haven’t fixed the match on the DB yet .-.
User page done! You can now see all of the matches and friends of other people, along with a small list of stats. I’m thinking about implementing the ELO system. While I previously said that I wouldn’t be able to moderate people artificially inflating it (by selfbotting), having it without a leaderboard wouldn’t really make it that enticing to abuse. And personally, if there isn’t a leaderboard, I don’t think moderating it would be that important, what would the user gain more than having a bigger number on their profile? Still thinking about it though I’m not sure about adding custom profile pictures or really any other user generated content. It would be nice to have, but it also involves moderation in order to prevent anything inappropriate from being uploaded (i mean, we all know how the internet works :/) Below is a small video showing the user page, along with the bot auto accepting any new friend requests :P
ELO system! let’s go!! At the end, I did add an ELO system. Well, it’s not really ELO, it’s Glicko . I used the glicko2-lite library, which is stateless unlike other glicko implementations I found. I wanted a stateless one because I’m saving the data on the DB anyways, so making it stateful would only make it use more resources (primarly RAM). You start with a rating of 1500. After every match your rating will go up/down depending on whether you win or lose. How much you win or lose depends on how many matches you have played (the first one are worth a bit more to get you to the optimal points), and depending on the rating of your oponent. If you win against someone with a bigger rating than yours, you’ll gain way more points than if you win agains someone around your rating or less. I also added a settings page so you can change your name and username, along with a page to change your password. Below is a small video showing how the ELO Glicko system works: What remains is just to also add the name/username change for bots and improve the docs. I also made a Stockfish bot which you can try to play against. (Keyword: try , because it’s probably almost impossible)
Well, that took way longer than expected. The docs are way more polished now! All endpoints have example responses so you know exactly what each one will return, along with a small description. HTTP endpoints that trigger a WebSocket event for any user (like friend requests, challenges, match moves, etc), mention it on their descriptions. I also added some headers to the PGN strings, as they were previously filled with “?“s. This mostly took this long due to a LOT of issues with the account deletion process, but it works perfectly now. When you delete your account, your user is replaced in all the matches you’ve played to @deleted_user . This is to keep the match data available even after any user deletes it. After that, your account is deleted from the DB. Due to how the DB is configured with cascade deletes, that will delete all other rows that reference the user (friendships, friend requests, pending challenges, etc.) except for matches, as they no longer reference the user that is being deleted. I’m am quite close to the ship, which I’m kinda nervous about. I want the project to be in the best state and as polished as possible, without any bugs (well, any known bugs lol). Which is mostly the reason this project as a whole has taken soo long :/ Below is a small video showing the docs with the example data. At the end is the delete account flow, which have lot’s of confirmations as it currently doesn’t send an email. I’m thinking it probably should send one lol See you in the next (and probably last) devlog! (I fixed my OBS settings, the video should look a bit better than the previous ones :P)
Hewo! Last devlog till ship here (i think) I didn’t think I’d add anything else, but you can now forfeit matches or ask the opponent for a draw! They just seemed like something that should exist on the shipped project… Each player can request only one draw per match, so you think before you do it. why did i do it like that? well, asking for a draw pauses the whole match, an neither player can do any moves until the draw request is answered. in order to prevent one of the users spamming draws, i simply limited to only one per match. also, ending the match in a draw doesn’t mean that your ELO isn’t affected, it is affected. depending on the elo of your opponent, you’ll lose/earn elo on a draw, just as you’d do on a stalemate. I also added a piece promotion selector. i forgot i made it so it always chose the queen by default, but in some matches that might not be wanted, so now you get a popup to select the piece you want to promote to well, i don’t really know if i’m missing any other things, as that was all that was i had on my TODO list, so i guess i’ll start setting up the prod environment below is a video showing the forfeit and draw flows, along with the promotion popup. don’t worry about the first match being a valid checkmate but the match continued being active, i already fixed that bug but it didn’t apply to already affected matches :P i also sped up (is that the correct way to say it?) and cutted out some parts of the video to prevent it from being 6 minutes of me playing the match lol
Well, I set up the domain + reverse proxy and all kind of problems arose. I changed the package so you only need to instantiate the class without any arguments to use the default server over at https://chessnow.angelcmh.com though you can still pass an optional baseUrl in case you selfhost your own instance at first nothing worked, but then i noticed that I had a port conflict on my VPS with two things trying to use the same port lol i also tried to improve SEO on the main page as much as I could, though im not an expert on that. i also added an official bots section so you can challenge some bots without having to look at any docs to find them shipping in some minutes!
Hewo! This devlog was unexpected. I prepared a small video/trailer so I had something to upload when shipping the project, and when I tried to upload it I realized that you can only upload images when shipping a project :/ So here is that video as it won’t go to waste. I tried my best, hope my voice doesn’t sound awkward, I’m not a native speaker Also, check out the shipped project! As I probably shipped it right after this devlog
aaaa, so… long time no see! also, super star!!! So, I’ve began doing some things that I’ve meant to do before, but completely forgot as I got quite busy… lol I fixed an error which made the TUI not work on Linux, it works now! It was an issue as I was building on NixOS, and the linker path was on my /nix/store folder instead of the usual /lib one. that means it also broke for me after an update lol You can now configure challenges! This needed to be implemented on all clients, hence why it took a little while lol Select whether you want to start with white, black or random. The opponent will be able to see which color you selected before accepting the challenge though! :P Also, meet unrankeds! You can now also mark a match as unranked so it doesn’t affect your ELO ratings at all . Take into account that there is decay though! so if you don’t play ranked for a while, your next match ELO diffs might be a little bit different Fun fact: The starting color option has been available on the API for a while already, even documented. so, if you did API calls directly, you could have selected a starting color and the opponent would not have been able to notice! as none of the clients showed it. except for the Discord bot… I think… or if you requested the match through the API too ;D
Ships:
This is Chess Now! A real-time chess API with challenges handling, matches, friends, and much more! The idea is for anyone to be able to make a custom client, bot or integration with the platform, thus it’s as open as it can possibly be while trying to also stay secure. One of the biggest parts of the proyect was doing that many clients, especially the TUI client. While I learned Vue doing the TUI client, I found it’s behaviour kinda… unintuitive in some way I can’t really explain. Though at the end I felt that Vue is better than React by a mile (now I only have to try out Svelte) I am proud of how well structured the API is, and while I’m sure it can be improved, the bases feel pretty solid. I’m also proud of how much I learned doing this. I mean, I tried a lot of things I’ve been meaning to try for some time already: Vue, Better Auth, Drizzle ORM, actually deploying a Next.js app, React mail + Resend, Elysia, and Fumadocs. And probably something else I already forgot about hehe Would I do something different if I started right now? Probably, but mostly just to try out even more things, which is the main reason why I do most of my projects. If you want to try out the project, you should probably just check the instance hosted at https://chessnow.angelcmh.com/ , create an account and play against a bot. You should also check out the docs, or the npm package over at https://www.npmjs.com/package/@chess-now/api if you are interested. Thanks for reading till the end, have a great day! ;P...
|
1792835668.0999582
|
1792922068.0999582
|
|
1172
|
Unity Motion Matching System by @NintedoAsh12 | St Unity Motion Matching System by @NintedoAsh12 | Stardance...
|
NintedoAsh12
|
0
|
140.12
|
0
|
[]
|
[{"post_id": 81951, "ts": "202 [{"post_id": 81951, "ts": "2026-10-23T04:55:45Z", "hours": 4.4681, "text": "I finished all the code comments. I know. WOW 4 HOURS OF CODE COMMENTS yeah but I was also cleaning some other parts of it up and fixing some of the indentations. anyways that’s done now only a few more things till we get to ship! but also a note to other devs like me… please comment your code while writing it because I have gotten in the habit of not doing that and as seeing here by 4 hours logged of code comments PLEASE JUST DO IT WHILE WRITING IT BECUASE THIS SUCKS"}, {"post_id": 81124, "ts": "2026-10-19T23:48:27Z", "hours": 3.2969, "text": "Not much changed I made an Itch.io for the demo. I also started adding comments to all of my code yeah its hard to remember what does what but I also got the dataset editor done or almost done. then its onto the matcher and then the trajectory maker then we are done there and then onto a tutorial of how to use it then we are done"}, {"post_id": 80294, "ts": "2026-10-17T05:11:06Z", "hours": 4.6256, "text": "OK its fixed now but first test build didn’t go so well (see video) the problem was unity wasn’t saving any of my data running in the background because it wasn’t serialized. I also organized project files plus removing unneeded scripts (I reuse code a lot and sometimes things get replaced and just left there) I also if you can’t tell from the video a dropped frame counter to show matches that where canceled because the current frame was good enough to meet the cost threshold. anyways I think now most of what I have to do it package everything up into… well a package then send it off into the world for you the hack clubbers to mess around with. EDIT: I added a demo for Windows to the github if you want to try it early"}, {"post_id": 79321, "ts": "2026-10-13T14:51:10Z", "hours": 2.2531, "text": "The demo UI has been finished and it works quite well! I have a few more things to add like a looping map so that the Character doesn’t just walk off the end of the world and die plus some more main menu stuff I think too. anyways progress has been fast so far so expect more soon!"}, {"post_id": 78971, "ts": "2026-10-12T06:56:58Z", "hours": 5.7781, "text": "LOTS OF DEBUG UI COMEING YOUR WAY! I have added lots of UI stuff to display internal values such as costs as stuff. I have also added the ability to adjust motion matching settings from in the demo so when I upload it to itch.io all of you can play with the system as much as you want and tell me how bad you broke it. to do wise its a bit more UI (some of it doesn’t work or I want it to look better) and then the data points kinda like the unity gizmo stuff I have been showing you here on star dance. anyways its like 1 AM I should have gone to bed HOURS AGO so I’mma go brush my teeth and work some more tomorrow…. my keyboard is starting to fail :-< Goodnight y’all. or good morning to the folk around the world like in Norway or something"}, {"post_id": 78190, "ts": "2026-10-09T22:29:56Z", "hours": 1.3486, "text": "I finally made a git hub repository. I also made a new unity project and moved everything into it to clean everything up for the demo I am starting to work on. I also made Bone Rotation matching but it doesn’t contribute all that match. I should be ready to ship/ Have a demo ready soon"}, {"post_id": 77746, "ts": "2026-10-08T04:44:35Z", "hours": 6.5831, "text": "OK OK IT WORKS ENOUGH OK DONT TOUCH IT I’m a fraud you guys. I couldn’t figure out the velocity thing so I…. I USED AI! I SWARE I HAVENT BEEN THIS ENTIRE TIME…. ok heres what happened I couldn’t figure it out so I had some AI going through some Motion Matching related thesis stuff and explaining it to me and then I ended up giving it a small chunk of my matcher code use to generate the cost and it pointed out my problems….. so 1. The way I was calculating local position was wrong which is funny because when I do it the other way it also breaks but then that leads into the second part. 2. I was inverting data and that was what was keeping the system working for some reason awhile back and even when I tried removing it a bit ago it broke which was really weird. anyways if you change both of them they fix each other so YAY I guess… sorry for the AI usage. it didn’t re write my code for me though I hand fixed my problem which I can proudly say all of the code is all hand typed…. (other then stuff I copy and pasted and or reused from other code I wrote… TRUST ME I NEVER WANT TO REWRITE UI SYSTEMS please unity fix the uitoolkit its bad)"}, {"post_id": 75040, "ts": "2026-09-28T23:29:30Z", "hours": 4.0911, "text": "Working on velocity matching. its kinda working now better then it was but its really weird that it slows the character down so much but its better then random snapping poses"}, {"post_id": 72963, "ts": "2026-09-24T03:42:38Z", "hours": 3.4628, "text": "HOLY CRAP IT WALKS!!! I can’t believe it. I am so FLIIPING happy ITS MOTION MATCHING! THAT IS ACTAUL REAL MOVEMENT AND NOT JUST WIGGLES!!!!! AAAAAAHHHHHH!!!!!! HECK YEAH!!!!!!! ok so in reality its still not fully working. I had to disable bone velocity matching completely to get it to work but I think I figured out why it doesn’t work. for trajectory matching I realized that the character is scaled to 1.83 instead of just 1 so that will affect the matching and I have to implement an offset (and I did it for the bone positions too but I never did it to the velocities) anyways I think I will fiddle with it till that work epeshly since STARDANCE WAS EXETENDED UNTILL OCTOBER 30TH THANK YOU HACK CLUB!!!! Video is of it working, not working and me playing with settings."}, {"post_id": 70896, "ts": "2026-09-19T23:38:42Z", "hours": 9.4997, "text": "WE HAVE HIT 100 HOURS LOGGED!!!! wow the amount of time I spent on this is CRAZY! anyways we have had little improvements on the matcher I have been trying to get it to give me a full animation but it refuses instead kinda just wiggling its arms up and down pretending to be a bird OR snapping between poses that have NOTHING to do with walking. I have spent the last few hours attempting to calibrate it to work as well as writing a better dataset debug script so then it will be easier to visualize what every frame of animation contains Data-wise so then I can attempt to manually match frames and see what the matcher ranks them vs what it normally matches so the I can maybe weight it or bias it a bit. we only have a week and bit left LET LOCK IN!!!!!!!!!!!!!!!!!!!!"}, {"post_id": 67146, "ts": "2026-09-13T06:34:17Z", "hours": 6.7892, "text": "we have done it! MOTION MATCHING FROM SCRATCH! the last week has been crazy. I have found more data from a unity demo of a long discontinued motion matching asset and I have retargeted the animation in blender and brought it into unity with proper root motion. since this was my first time using more then one animation clip with EVERYTHING I expected bugs and errors AND THAT I GOT. I had to go in and rewrite a really small chunk of how the dataset maker compiles the trajectory data. I had to make the matcher actually handle more then one clip as well as dig into a null reference error it was giving me so I had to dig into it more and learn that the dataset was still flawed and go back and do more to the editor AND THEN ONCE the data was good now the matcher scales the character to like 300% or something and I never even told it to touch scale so now I have to properly set up multi-animation unity playables since before I didn’t know that you couldn’t just set all the blend weights to one and hope for the best. now finally after all of that. it appears to match and hold onto a frame. I have a few last features to implement into it that being velocity matching so then it wont just hold a frame but understand that arms move and swing. as well as debug info for both me while testing as well as for you my dear hackclubbers so when I ship you all can see a bit into the inner workings of it. thank you for coming with me on this journey I never expected it to go this long but it feels good to have at least one person interested in what I’m working on for once and not just get shrugged off. - Ash ❤️ Video is of the matcher working photos are of the scaling issue"}, {"post_id": 63393, "ts": "2026-09-07T06:09:06Z", "hours": 5.9814, "text": "sheesh I’ve done a bit in the last week. So we have added the ability to match bones in the matcher got the matcher to handle multiple animation clips and I guess that’s it… I honestly thought I did more… on paper it doesn’t actually seem like that much.. huh. OH WELL! I also found a MASIVE collection of animation from a unreal engine demo project so I’m probably going to extract that and retarget TONS of that over the next few days because imma say it the problem with the matcher is the lack of good data at this point. Pink/Purple - Animation frame data position Red/green - predicted trajectory"}, {"post_id": 60036, "ts": "2026-09-02T04:33:58Z", "hours": 1.2344, "text": "OK OK OK IT WORKS!!!!! so we got bone positions to work. I’m done for the day. anyways I literally just had to flip my subtraction around… yes… its stupid…. anyways went from this: RootPositions[i][j] - BoneTransform.position to this: BoneTransform.position - RootPositions[i][j]. ok mic drop… I did say I would ship by the end of the month of august and clearly that didn’t happen but it will (hopefully) be this week Pink sphere is the foot pos from data set Blue is the other models foot pos not from dataset then red is root position (Also 2 devlogs in one day! I’m on fire!)"}, {"post_id": 59736, "ts": "2026-09-01T17:45:26Z", "hours": 8.6739, "text": "IM SO CLOSE!!!!!!!!! LIKE LATER TODAY (maybe can’t promise anything or the programing gods will kill me) I WILL HAVE MOTION MATCHING IN A GOOD DEMOABLE STATE FOR ME TO SHIP! I have got the bone data to work and actually spit out something useful by changing my whole approach and instead of sampling curves (which could work I just gotta deal with all the weird problems) I have created a unity scene in memory it spawns in the rig samples all the bone transform.positrons making unity do all the math and hard work before it returns me world positions of all of the bones I told it to track. the only problem I am having now is that I got to convert those world positions back into local position so that they can work anywhere (and yes I just converted them from local position to world position and this might sound counter intuitive but I need to change the parent of the local position) I remember this being easy for trajectory matching but maybe since this is WAYY more sensitive to rotation I need to try a different approach. the final stretch is approaching us dear hack clubbers. I got the rest of today then school starts tomorrow. LETS! LOCK! IN! YEAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!!!! btw the pink sphere is the foot potion… or what should be the foot position. Blue is the actual foot position. Red is root Position"}, {"post_id": 54275, "ts": "2026-08-25T05:45:45Z", "hours": 8.4231, "text": "Note to self… don’t freehand code… plan everything out on paper because I’m just experiencing Murphey’s law at this point :-< so…. the data that I didn’t know if it was right or not was incorrect… VERY incorrect (kinda) so that data was in local space I need it in world space but to convert it you either need to do the conversion through a transform (which requires a transform to reference) or do what I am doing (since I don’t have a easily accessible transform (unless I’m stupid and don’t know of one)) so I am going up the paths and for each object extracting their position and transforms (in local position) then adding it up until I eventually hit the root which would be in world space so now its converted (I’m doing the same for rotation too with more math) anyways my code for this entire project was never pre planned and I never normally pre plan but this is one of the times where once it works I’m going to figure out how it works (because who am I kidding I don’t really know) then I’m writing all of it down on paper and optimizing the loops and everything before jumping back into the editor to re-write the bake() function from scratch leading to MUCH BETTER READABILTY and hopefully speed because it takes a good 5 seconds from my for loops and brute force. anyways if you cant tell this is VERY BORING WORK and I really just want it to work SO BADLY because once I get everything in world position the next steps should be really easy but I shouldn’t say that because at this point if I do I might jinx myself"}, {"post_id": 46135, "ts": "2026-08-13T04:38:45Z", "hours": 3.9642, "text": "WOW JUST WOW!!! So I toured the Vicon Set-up at my local univercity and it was amazing. I loved every moment of it. they even put markers on me and captured some data (not any I can use for motion matching but still WOW THAT TECH IS COOL) In more on topic news I got the dataset maker to output bone data into the database. is it the correct data… idk but I will figure that out! (Photo of me standing forward is just a general photo with the markers. photo of me looking away from camera with hand in air is me showing off all the retroreflective markers (and EMGs) then there is a photo of the compiled dataset)"}, {"post_id": 43104, "ts": "2026-08-08T06:02:03Z", "hours": 2.9892, "text": "THERE WE GO! finally we can select what bones we want to track from a nested dropdown. now I just need to implement it into the baking logic so I can get a finished dataset out of dataset maker. perfect timing too. I have gotten a few emails back from the university and they are willing to give me a tour of the Vicon system and there may be a chance one day to capture data on it so hopefully everything works out and I can capture on the same tech that AAA game studios use which would be a dream come true and would blow my mind."}, {"post_id": 40564, "ts": "2026-08-04T06:14:32Z", "hours": 3.9083, "text": "There we go. I think I finally got it working. JEEZ UNITY NEEDS BETTER DOCUMENTATION ON THE UI TOOLKIT LIKE WOW ITS THAT BAD…. anyway I got the custom lists working now I just gotta add the actual bone selections then write in the logic to add everything to the datasets. if you look at the photo that’s roughly how it will look to use the dataset maker. not sure if I mentioned this in the last devlog but I finally messaged my local university to see if they will let me either tour or use their vicon system so I’m hoping something comes of that because that would be really cool. anyways its really late I pulled another programmer and stayed up till midnight working. see yall later."}, {"post_id": 38041, "ts": "2026-07-31T06:49:07Z", "hours": 7.1853, "text": "Pretty boring stuff happening right now. I have been working to implement bone positional and velocity tracking into the dataset builder but since I wanted to use a custom class to do it the unity UI Toolkit decided to pick a fight and be really annoying to do it in. if anyone wants to write a custom class in unity and have it show up in a list view use this: link I also am working on processing some mocap data but its not going so well right now and I am getting pretty tired with dealing with these markerless systems so we will see if my local university will let me use or even tour their 10 cam Vicon setup (cross your fingers for me) anyways I still have one mocap session to prosses so I will probably do that tomorrow since I can just let it run while I am doing other things. also I just watched these videos where this guy is building open sourced optical mocap cameras and I think I might just funnel all my hack club hours into pcb credits to build a few of them. part 1 vid: https://youtu.be/kYVqL_DqBis?si=CPKxKiLa8cxf3M1t Part 2 vid: https://youtu.be/WJ13C_1hDIA?si=hMlQ8u5ofEBf-cFS"}, {"post_id": 34122, "ts": "2026-07-24T16:47:29Z", "hours": 8.3331, "text": "#MOTION MATCHING SYSTEM V1 DEVLOG ok its been a few days and we have gotten a few things done including started work on feet and hand bone tracking (the secret sause to motion matching) I finished making the large Charuco Board so I can calibrate FreeMoCap. so its coming along… slowly but that’s ok. the hardest part of all of this is the motion capture though since my setup is really bad (I wish my city had a capture studio somewhere even if it was small)"}, {"post_id": 29976, "ts": "2026-07-17T06:02:22Z", "hours": 10.1758, "text": "sheesh its been awhile since I last posted a devlog. MOTION MATCHING SYSTEM V1 - HIP Better matching Change Log/Updates Matching changed from world space to local space started work on recording motion capture I got more consistent matching now thanks to changing how it store the data in the dataset and how it matches to it. originally the future trajectory positions where stored as world space values in the dataset and the matcher would match it to the players world space values. if you know how transform systems work in game engines or blender you might know that because of that I had to be in the EXACT same position in the world that the data was recorded (you can see this in my last devlog where I kinda wiggle around in a few areas and get flurries of matches) in which… doesn’t work very well for this because if you had to record your self walking forwards backwards and side to side for ever single value in the game world that would take you FOREVER! so I switched it to local space which is basically where you are in the world based off of something. so like instead of 0, 0, 0 being the root of the world it would be where the character is so the trajectory positions are based around the character transform… do you know what I mean? I hope so because I cant think of a better way to explain it at the moment. Right now the biggest thing holding me back is the actual motion capture data that I am feeding into this thing because the recording I downloaded… is really bad. so I have been experimenting with a free open sourced software called FreeMoCap which is a marker less triangulation based motion capture software. I will report back when I get some good results out of it (hopefully soon) TODO hand and feet position and velocity matching 6 to 8 camera motion capture data finish Charuco board for FreeMoCap camera calibration Video is of the new matching in action the red (and 1 green) points are the players future predicted trajectory the pinkish purple points are the animation fames current trajectory. they should be aligned most of the time"}, {"post_id": 19015, "ts": "2026-06-30T05:40:05Z", "hours": 2.9392, "text": "FIRST MATCH!!! WE DID IT! This past week I started working on code to compare and chose a frame to play (the actual matcher) and it works… Kind of? YES IT MACHES… Is it a good match? HECK NO!!!! BUT IT MATCHES!!!! AND THAT JUST MADE 9 MONTHS OF WORK WORTH IT AS I HYPERVENTILATE WATCHING MY MATCHER STUGGLE BADLY LAGING EVERYTHING BUT IT WORKS! now the next step is making it work… properly. the video is of it trying to match. i kinda move around a bit to get it changing data but you can tell when it finds… something because the character will lock onto a frame or start playing the animation."}, {"post_id": 15199, "ts": "2026-06-24T18:20:54Z", "hours": 10.0492, "text": "WE HAVE A WORKING DATASET! Hello hack club! Didja miss me? NO?! WELL TOO BAD!!!!! I now got my motion capture data outputting a dataset with trajectory samples and root positions which is a VERY big step forwards. so as I have mentioned in the past motion matching is literally what MOTION MATCHING is doing. Matching the motion. I have had player predicted trajectory working since before stardance even started but only recently have we started to make progress on the motion capture data that we can MATCH to the players predicted trajectory. the set up I have going on for running the motion data extraction is really cool if I do say so myself. I have a custom editor menu where I can add animation clips and tell it what times in the future I want the trajectory to sampled from then it will compile everything into a scriptable object. plus I also added cool loading bars! anyways we are getting very close to having actual motion matching made from scratch. wow. 9 months this has took so far. its been fun. well… technically not done yet so lets keep up the good work and build the matching algorithm"}, {"post_id": 8844, "ts": "2026-06-14T18:22:33Z", "hours": 7.4806, "text": "DataSet Baker V1 Alpha - HIP is in the works!!!!! Now you might be asking why it’s named HIP, and unless you are from Canada you will most likely not know who I am going to talk about, but the motion matching editor versions are going to be named after Tragically Hip album names (the first one is self titled so HIP). Anyways, in the last week I have gotten a lot done. I started work on actually figuring out the logic to get the trajectory out of the animation (and I don’t remember if I talked about this before but there were 2 ideas I had. 1 is I put the animation’s root motion through a lot more math to get a proper predicted trajectory just like how the input trajectory works. Or just sample the actual future, but then you would get problems like a 90 degree turn where the animation isn’t even anticipating it yet but the trajectory says it should be.) I actually got really confused and stuck so I emailed someone. That someone is Daniel Holden, the creator of Learned Motion Matching (and he runs the website where I learned the first input trajectory math). And WOW!!!! HE RESPONDED: Hi Asher, Sorry for the late reply, I have been travelling the last couple of days. First of all I just wanted to say that it is awesome that you are looking into advanced animation techniques like these already. (email removed for space) (i might put it in the comments if you want to read it) I hope that helps! All the best, Dan LIKE HOLY COW!!! I am talking to a LEGEND!!!! Normal motion matching works as such: you got 2 trajectories, a predicted one from the input then a true trajectory from the animation, then you load all that animation into RAM and use a matcher to play the correct frame. But what he made was use a neural network instead of the matcher and database WHICH SHRUNK RAM USAGE LIKE CRAZY (and I might have to get working on something like that sometime soon because RAM prices don’t appear to be dropping anytime soon). Anyways, in the email he said that my second idea was actually right. The idea that I thought was wrong, but he says is just a sad limitation of it and nobody has figured it out yet (yet…). So that will make life easier because I WILL BE ABLE TO FINISH THE DATASET STUFF HOPEFULLY IN A WEEK!!!!!!! And to make that dataset I started messing around with Unity’s UI Toolkit and custom editors. So now I have several custom drop downs and then you can open the MOTION MATCHING DATASET EDITOR where you can define all of your animation clips and once I write the rest, be able to press the bake button. It will process every frame and then spit out a Scriptable Object or database. Now hopefully writing the rest will be easier than writing the UI code because the UI stuff is powerful but not like normal runtime coding."}, {"post_id": 5233, "ts": "2026-06-08T22:55:23Z", "hours": 1.5761, "text": "HELLO STARDANCE!!!!!! I THINK I FIXED IT!!! Now I am not going to even mention that root motion is working this time because if you have been following along you know that the second “IT WORKS!” comes out of my mouth something breaks. So last devlog I said I thought it was working but… my character was half way in the floor. after a bit of fiddling around and some google I discovered that the problem was that I had put the root bone in the hips of the character. And apparently in unity that’s a big no no. So one hour and eight minutes of blender B.S later I Fixed it (hopefully). I ended up using a blender addon called RootMaker to create a proper root bone at the feet where unity wants it. then I used the addon to transfer all of the root motion data from the hips into the new dedicated root bone. so now hopefully unity will be happy that it has a proper root bone to work with and wont try to shove my character into the earths mantle. The crazy part of all of this is that I have been doing more of blender and fighting animation during Stardance than actually working on the motion matching so if this continues to work I can get to work on what I am supposed to be doing; working on the actual motion matching. since I am the music variety of nerd I spend most of my time coding jamming to music and I think I started measuring my programing time in albums rather then hours XD. Over the course of just this weekend I have listened front to back to: 21st Century Breakdown - Green Day American Idiot - Green Day Back in Black - AC/DC Black Holes and Revelations - Muse The Black Parade - My Chemical Romance and if you add all of the runtimes of those albums up it matches very close to the time logged in Stardance so it might work XD. anyways I am going to go stretch my legs then maybe play some of The Last of Us Part 2 (which is very appropriate since it too uses motion matching as its animation system)"}, {"post_id": 5031, "ts": "2026-06-08T18:06:54Z", "hours": 1.1372, "text": "at this point I should stop saying that I have root motion working because every time I say that I jinx it and something falls apart. so I set everything up wrong (of course) so I need to pull all of the rigs apart once more to fix some errors that I made there and how I set up the root bone (its set at the hips not at the feet). so its been a fun ride (not) plus my laptop keeps trying to update my graphics drivers and every time it crashes unity, blender, plus all of the audio drivers with it. XD. I hope after today I can start work back up on motion matching and not dealing with blender and its shenanigans."}, {"post_id": 4545, "ts": "2026-06-07T21:49:20Z", "hours": 0.8836, "text": "Ok. I may have celebrated a little bit early. but I think I finally got root motion working for real this time. the problem was a combination of blender being blender, transformation offsets, weird scaling and just the names of objects/bones being wrong. though I did get an awesome video from when I was testing and comparing two animations together and root motion sent one of them into the floor so we got a beautiful video of one guy riding the other guys head. anyways lets hope that this keeps working because I just want to get my math mathing and dataset datasetting"}, {"post_id": 4362, "ts": "2026-06-07T17:35:54Z", "hours": 1.3164, "text": "This morning I got root motion finally set up in unity. since that now works I can begin working on a data set for the motion matching. I guess I never really explained how motion matching works in any of my other devlogs so I am going to do that right now: Motion matching is a animating technique where instead of using classic state machines that go if walking play walking animation they go this is your current position, velocity, trajectory, and a lot more and try to MATCH that MOTION to a large database of motion captured animations. so what I will be working on next is building that database/dataset of animations and the code to pull the current position, velocity, trajectory out of the animations then store each frame in a dataset for the matching code to search though to find one that matches what the character is currently doing on screen. its sounds kinda easy but trust me unless you are a big math/physics person (which I am not) its quite difficult to figure out how it al l works (plus you also need to know animations and blender which SUCKSSSS!) Also the video is of root motion NOT WORKING!"}, {"post_id": 1659, "ts": "2026-06-03T19:53:43Z", "hours": 1.2667, "text": "fighting unity and blender animation systems"}, {"post_id": 335, "ts": "2026-06-01T17:11:13Z", "hours": 0.4014, "text": "I got input trajectory working. The reason it wasn’t before was that movement and the prediction where in different update types (one was in fixed update and one was in regular update) so that works really well now. Now I am setting stuff up to make a animation dataset which involve me modifying my trajectory code to make it work with PRE RECORDED animations Devlogs From flavortown: Devlog 1: Lots of research and trial and error. I have spent all this time looking for an algorithm that works to generate a predicted trajectory from an input. I think I found the right one (I haven’t tested it yet) but if not then I know that I am looking in the right place at Daniel Holden’s articles on spring dampers check it out here: https://theorangeduck.com/page/spring-roll-call#controllers Devlog 2: I got the Trajectory prediction working!!!! after all of that math, research, and pain. it works! I wish I could truly express in words how happy I am. everything is based off of the work of Daniel Holden you can find it here: https://theorangeduck.com/page/spring-roll-call#controllers Devlog 3: Fixed jittering issue by adding a lerp to desired trajectory."}]...
|
Title: Unity Motion Matching System by @NintedoAsh Title: Unity Motion Matching System by @NintedoAsh12 | Stardance
Devlogs:
I got input trajectory working. The reason it wasn’t before was that movement and the prediction where in different update types (one was in fixed update and one was in regular update) so that works really well now. Now I am setting stuff up to make a animation dataset which involve me modifying my trajectory code to make it work with PRE RECORDED animations Devlogs From flavortown: Devlog 1: Lots of research and trial and error. I have spent all this time looking for an algorithm that works to generate a predicted trajectory from an input. I think I found the right one (I haven’t tested it yet) but if not then I know that I am looking in the right place at Daniel Holden’s articles on spring dampers check it out here: https://theorangeduck.com/page/spring-roll-call#controllers Devlog 2: I got the Trajectory prediction working!!!! after all of that math, research, and pain. it works! I wish I could truly express in words how happy I am. everything is based off of the work of Daniel Holden you can find it here: https://theorangeduck.com/page/spring-roll-call#controllers Devlog 3: Fixed jittering issue by adding a lerp to desired trajectory.
fighting unity and blender animation systems
This morning I got root motion finally set up in unity. since that now works I can begin working on a data set for the motion matching. I guess I never really explained how motion matching works in any of my other devlogs so I am going to do that right now: Motion matching is a animating technique where instead of using classic state machines that go if walking play walking animation they go this is your current position, velocity, trajectory, and a lot more and try to MATCH that MOTION to a large database of motion captured animations. so what I will be working on next is building that database/dataset of animations and the code to pull the current position, velocity, trajectory out of the animations then store each frame in a dataset for the matching code to search though to find one that matches what the character is currently doing on screen. its sounds kinda easy but trust me unless you are a big math/physics person (which I am not) its quite difficult to figure out how it al l works (plus you also need to know animations and blender which SUCKSSSS!) Also the video is of root motion NOT WORKING!
Ok. I may have celebrated a little bit early. but I think I finally got root motion working for real this time. the problem was a combination of blender being blender, transformation offsets, weird scaling and just the names of objects/bones being wrong. though I did get an awesome video from when I was testing and comparing two animations together and root motion sent one of them into the floor so we got a beautiful video of one guy riding the other guys head. anyways lets hope that this keeps working because I just want to get my math mathing and dataset datasetting
at this point I should stop saying that I have root motion working because every time I say that I jinx it and something falls apart. so I set everything up wrong (of course) so I need to pull all of the rigs apart once more to fix some errors that I made there and how I set up the root bone (its set at the hips not at the feet). so its been a fun ride (not) plus my laptop keeps trying to update my graphics drivers and every time it crashes unity, blender, plus all of the audio drivers with it. XD. I hope after today I can start work back up on motion matching and not dealing with blender and its shenanigans.
HELLO STARDANCE!!!!!! I THINK I FIXED IT!!! Now I am not going to even mention that root motion is working this time because if you have been following along you know that the second “IT WORKS!” comes out of my mouth something breaks. So last devlog I said I thought it was working but… my character was half way in the floor. after a bit of fiddling around and some google I discovered that the problem was that I had put the root bone in the hips of the character. And apparently in unity that’s a big no no. So one hour and eight minutes of blender B.S later I Fixed it (hopefully). I ended up using a blender addon called RootMaker to create a proper root bone at the feet where unity wants it. then I used the addon to transfer all of the root motion data from the hips into the new dedicated root bone. so now hopefully unity will be happy that it has a proper root bone to work with and wont try to shove my character into the earths mantle. The crazy part of all of this is that I have been doing more of blender and fighting animation during Stardance than actually working on the motion matching so if this continues to work I can get to work on what I am supposed to be doing; working on the actual motion matching. since I am the music variety of nerd I spend most of my time coding jamming to music and I think I started measuring my programing time in albums rather then hours XD. Over the course of just this weekend I have listened front to back to: 21st Century Breakdown - Green Day American Idiot - Green Day Back in Black - AC/DC Black Holes and Revelations - Muse The Black Parade - My Chemical Romance and if you add all of the runtimes of those albums up it matches very close to the time logged in Stardance so it might work XD. anyways I am going to go stretch my legs then maybe play some of The Last of Us Part 2 (which is very appropriate since it too uses motion matching as its animation system)
DataSet Baker V1 Alpha - HIP is in the works!!!!! Now you might be asking why it’s named HIP, and unless you are from Canada you will most likely not know who I am going to talk about, but the motion matching editor versions are going to be named after Tragically Hip album names (the first one is self titled so HIP). Anyways, in the last week I have gotten a lot done. I started work on actually figuring out the logic to get the trajectory out of the animation (and I don’t remember if I talked about this before but there were 2 ideas I had. 1 is I put the animation’s root motion through a lot more math to get a proper predicted trajectory just like how the input trajectory works. Or just sample the actual future, but then you would get problems like a 90 degree turn where the animation isn’t even anticipating it yet but the trajectory says it should be.) I actually got really confused and stuck so I emailed someone. That someone is Daniel Holden, the creator of Learned Motion Matching (and he runs the website where I learned the first input trajectory math). And WOW!!!! HE RESPONDED: Hi Asher, Sorry for the late reply, I have been travelling the last couple of days. First of all I just wanted to say that it is awesome that you are looking into advanced animation techniques like these already. (email removed for space) (i might put it in the comments if you want to read it) I hope that helps! All the best, Dan LIKE HOLY COW!!! I am talking to a LEGEND!!!! Normal motion matching works as such: you got 2 trajectories, a predicted one from the input then a true trajectory from the animation, then you load all that animation into RAM and use a matcher to play the correct frame. But what he made was use a neural network instead of the matcher and database WHICH SHRUNK RAM USAGE LIKE CRAZY (and I might have to get working on something like that sometime soon because RAM prices don’t appear to be dropping anytime soon). Anyways, in the email he said that my second idea was actually right. The idea that I thought was wrong, but he says is just a sad limitation of it and nobody has figured it out yet (yet…). So that will make life easier because I WILL BE ABLE TO FINISH THE DATASET STUFF HOPEFULLY IN A WEEK!!!!!!! And to make that dataset I started messing around with Unity’s UI Toolkit and custom editors. So now I have several custom drop downs and then you can open the MOTION MATCHING DATASET EDITOR where you can define all of your animation clips and once I write the rest, be able to press the bake button. It will process every frame and then spit out a Scriptable Object or database. Now hopefully writing the rest will be easier than writing the UI code because the UI stuff is powerful but not like normal runtime coding.
WE HAVE A WORKING DATASET! Hello hack club! Didja miss me? NO?! WELL TOO BAD!!!!! I now got my motion capture data outputting a dataset with trajectory samples and root positions which is a VERY big step forwards. so as I have mentioned in the past motion matching is literally what MOTION MATCHING is doing. Matching the motion. I have had player predicted trajectory working since before stardance even started but only recently have we started to make progress on the motion capture data that we can MATCH to the players predicted trajectory. the set up I have going on for running the motion data extraction is really cool if I do say so myself. I have a custom editor menu where I can add animation clips and tell it what times in the future I want the trajectory to sampled from then it will compile everything into a scriptable object. plus I also added cool loading bars! anyways we are getting very close to having actual motion matching made from scratch. wow. 9 months this has took so far. its been fun. well… technically not done yet so lets keep up the good work and build the matching algorithm
FIRST MATCH!!! WE DID IT! This past week I started working on code to compare and chose a frame to play (the actual matcher) and it works… Kind of? YES IT MACHES… Is it a good match? HECK NO!!!! BUT IT MATCHES!!!! AND THAT JUST MADE 9 MONTHS OF WORK WORTH IT AS I HYPERVENTILATE WATCHING MY MATCHER STUGGLE BADLY LAGING EVERYTHING BUT IT WORKS! now the next step is making it work… properly. the video is of it trying to match. i kinda move around a bit to get it changing data but you can tell when it finds… something because the character will lock onto a frame or start playing the animation.
sheesh its been awhile since I last posted a devlog. MOTION MATCHING SYSTEM V1 - HIP Better matching Change Log/Updates Matching changed from world space to local space started work on recording motion capture I got more consistent matching now thanks to changing how it store the data in the dataset and how it matches to it. originally the future trajectory positions where stored as world space values in the dataset and the matcher would match it to the players world space values. if you know how transform systems work in game engines or blender you might know that because of that I had to be in the EXACT same position in the world that the data was recorded (you can see this in my last devlog where I kinda wiggle around in a few areas and get flurries of matches) in which… doesn’t work very well for this because if you had to record your self walking forwards backwards and side to side for ever single value in the game world that would take you FOREVER! so I switched it to local space which is basically where you are in the world based off of something. so like instead of 0, 0, 0 being the root of the world it would be where the character is so the trajectory positions are based around the character transform… do you know what I mean? I hope so because I cant think of a better way to explain it at the moment. Right now the biggest thing holding me back is the actual motion capture data that I am feeding into this thing because the recording I downloaded… is really bad. so I have been experimenting with a free open sourced software called FreeMoCap which is a marker less triangulation based motion capture software. I will report back when I get some good results out of it (hopefully soon) TODO hand and feet position and velocity matching 6 to 8 camera motion capture data finish Charuco board for FreeMoCap camera calibration Video is of the new matching in action the red (and 1 green) points are the players future predicted trajectory the pinkish purple points are the animation fames current trajectory. they should be aligned most of the time
#MOTION MATCHING SYSTEM V1 DEVLOG ok its been a few days and we have gotten a few things done including started work on feet and hand bone tracking (the secret sause to motion matching) I finished making the large Charuco Board so I can calibrate FreeMoCap. so its coming along… slowly but that’s ok. the hardest part of all of this is the motion capture though since my setup is really bad (I wish my city had a capture studio somewhere even if it was small)
Pretty boring stuff happening right now. I have been working to implement bone positional and velocity tracking into the dataset builder but since I wanted to use a custom class to do it the unity UI Toolkit decided to pick a fight and be really annoying to do it in. if anyone wants to write a custom class in unity and have it show up in a list view use this: link I also am working on processing some mocap data but its not going so well right now and I am getting pretty tired with dealing with these markerless systems so we will see if my local university will let me use or even tour their 10 cam Vicon setup (cross your fingers for me) anyways I still have one mocap session to prosses so I will probably do that tomorrow since I can just let it run while I am doing other things. also I just watched these videos where this guy is building open sourced optical mocap cameras and I think I might just funnel all my hack club hours into pcb credits to build a few of them. part 1 vid: https://youtu.be/kYVqL_DqBis?si=CPKxKiLa8cxf3M1t Part 2 vid: https://youtu.be/WJ13C_1hDIA?si=hMlQ8u5ofEBf-cFS
There we go. I think I finally got it working. JEEZ UNITY NEEDS BETTER DOCUMENTATION ON THE UI TOOLKIT LIKE WOW ITS THAT BAD…. anyway I got the custom lists working now I just gotta add the actual bone selections then write in the logic to add everything to the datasets. if you look at the photo that’s roughly how it will look to use the dataset maker. not sure if I mentioned this in the last devlog but I finally messaged my local university to see if they will let me either tour or use their vicon system so I’m hoping something comes of that because that would be really cool. anyways its really late I pulled another programmer and stayed up till midnight working. see yall later.
THERE WE GO! finally we can select what bones we want to track from a nested dropdown. now I just need to implement it into the baking logic so I can get a finished dataset out of dataset maker. perfect timing too. I have gotten a few emails back from the university and they are willing to give me a tour of the Vicon system and there may be a chance one day to capture data on it so hopefully everything works out and I can capture on the same tech that AAA game studios use which would be a dream come true and would blow my mind.
WOW JUST WOW!!! So I toured the Vicon Set-up at my local univercity and it was amazing. I loved every moment of it. they even put markers on me and captured some data (not any I can use for motion matching but still WOW THAT TECH IS COOL) In more on topic news I got the dataset maker to output bone data into the database. is it the correct data… idk but I will figure that out! (Photo of me standing forward is just a general photo with the markers. photo of me looking away from camera with hand in air is me showing off all the retroreflective markers (and EMGs) then there is a photo of the compiled dataset)
Note to self… don’t freehand code… plan everything out on paper because I’m just experiencing Murphey’s law at this point :-< so…. the data that I didn’t know if it was right or not was incorrect… VERY incorrect (kinda) so that data was in local space I need it in world space but to convert it you either need to do the conversion through a transform (which requires a transform to reference) or do what I am doing (since I don’t have a easily accessible transform (unless I’m stupid and don’t know of one)) so I am going up the paths and for each object extracting their position and transforms (in local position) then adding it up until I eventually hit the root which would be in world space so now its converted (I’m doing the same for rotation too with more math) anyways my code for this entire project was never pre planned and I never normally pre plan but this is one of the times where once it works I’m going to figure out how it works (because who am I kidding I don’t really know) then I’m writing all of it down on paper and optimizing the loops and everything before jumping back into the editor to re-write the bake() function from scratch leading to MUCH BETTER READABILTY and hopefully speed because it takes a good 5 seconds from my for loops and brute force. anyways if you cant tell this is VERY BORING WORK and I really just want it to work SO BADLY because once I get everything in world position the next steps should be really easy but I shouldn’t say that because at this point if I do I might jinx myself
IM SO CLOSE!!!!!!!!! LIKE LATER TODAY (maybe can’t promise anything or the programing gods will kill me) I WILL HAVE MOTION MATCHING IN A GOOD DEMOABLE STATE FOR ME TO SHIP! I have got the bone data to work and actually spit out something useful by changing my whole approach and instead of sampling curves (which could work I just gotta deal with all the weird problems) I have created a unity scene in memory it spawns in the rig samples all the bone transform.positrons making unity do all the math and hard work before it returns me world positions of all of the bones I told it to track. the only problem I am having now is that I got to convert those world positions back into local position so that they can work anywhere (and yes I just converted them from local position to world position and this might sound counter intuitive but I need to change the parent of the local position) I remember this being easy for trajectory matching but maybe since this is WAYY more sensitive to rotation I need to try a different approach. the final stretch is approaching us dear hack clubbers. I got the rest of today then school starts tomorrow. LETS! LOCK! IN! YEAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!!!! btw the pink sphere is the foot potion… or what should be the foot position. Blue is the actual foot position. Red is root Position
OK OK OK IT WORKS!!!!! so we got bone positions to work. I’m done for the day. anyways I literally just had to flip my subtraction around… yes… its stupid…. anyways went from this: RootPositions[i][j] - BoneTransform.position to this: BoneTransform.position - RootPositions[i][j]. ok mic drop… I did say I would ship by the end of the month of august and clearly that didn’t happen but it will (hopefully) be this week Pink sphere is the foot pos from data set Blue is the other models foot pos not from dataset then red is root position (Also 2 devlogs in one day! I’m on fire!)
sheesh I’ve done a bit in the last week. So we have added the ability to match bones in the matcher got the matcher to handle multiple animation clips and I guess that’s it… I honestly thought I did more… on paper it doesn’t actually seem like that much.. huh. OH WELL! I also found a MASIVE collection of animation from a unreal engine demo project so I’m probably going to extract that and retarget TONS of that over the next few days because imma say it the problem with the matcher is the lack of good data at this point. Pink/Purple - Animation frame data position Red/green - predicted trajectory
we have done it! MOTION MATCHING FROM SCRATCH! the last week has been crazy. I have found more data from a unity demo of a long discontinued motion matching asset and I have retargeted the animation in blender and brought it into unity with proper root motion. since this was my first time using more then one animation clip with EVERYTHING I expected bugs and errors AND THAT I GOT. I had to go in and rewrite a really small chunk of how the dataset maker compiles the trajectory data. I had to make the matcher actually handle more then one clip as well as dig into a null reference error it was giving me so I had to dig into it more and learn that the dataset was still flawed and go back and do more to the editor AND THEN ONCE the data was good now the matcher scales the character to like 300% or something and I never even told it to touch scale so now I have to properly set up multi-animation unity playables since before I didn’t know that you couldn’t just set all the blend weights to one and hope for the best. now finally after all of that. it appears to match and hold onto a frame. I have a few last features to implement into it that being velocity matching so then it wont just hold a frame but understand that arms move and swing. as well as debug info for both me while testing as well as for you my dear hackclubbers so when I ship you all can see a bit into the inner workings of it. thank you for coming with me on this journey I never expected it to go this long but it feels good to have at least one person interested in what I’m working on for once and not just get shrugged off. - Ash ❤️ Video is of the matcher working photos are of the scaling issue
WE HAVE HIT 100 HOURS LOGGED!!!! wow the amount of time I spent on this is CRAZY! anyways we have had little improvements on the matcher I have been trying to get it to give me a full animation but it refuses instead kinda just wiggling its arms up and down pretending to be a bird OR snapping between poses that have NOTHING to do with walking. I have spent the last few hours attempting to calibrate it to work as well as writing a better dataset debug script so then it will be easier to visualize what every frame of animation contains Data-wise so then I can attempt to manually match frames and see what the matcher ranks them vs what it normally matches so the I can maybe weight it or bias it a bit. we only have a week and bit left LET LOCK IN!!!!!!!!!!!!!!!!!!!!
HOLY CRAP IT WALKS!!! I can’t believe it. I am so FLIIPING happy ITS MOTION MATCHING! THAT IS ACTAUL REAL MOVEMENT AND NOT JUST WIGGLES!!!!! AAAAAAHHHHHH!!!!!! HECK YEAH!!!!!!! ok so in reality its still not fully working. I had to disable bone velocity matching completely to get it to work but I think I figured out why it doesn’t work. for trajectory matching I realized that the character is scaled to 1.83 instead of just 1 so that will affect the matching and I have to implement an offset (and I did it for the bone positions too but I never did it to the velocities) anyways I think I will fiddle with it till that work epeshly since STARDANCE WAS EXETENDED UNTILL OCTOBER 30TH THANK YOU HACK CLUB!!!! Video is of it working, not working and me playing with settings.
Working on velocity matching. its kinda working now better then it was but its really weird that it slows the character down so much but its better then random snapping poses
OK OK IT WORKS ENOUGH OK DONT TOUCH IT I’m a fraud you guys. I couldn’t figure out the velocity thing so I…. I USED AI! I SWARE I HAVENT BEEN THIS ENTIRE TIME…. ok heres what happened I couldn’t figure it out so I had some AI going through some Motion Matching related thesis stuff and explaining it to me and then I ended up giving it a small chunk of my matcher code use to generate the cost and it pointed out my problems….. so 1. The way I was calculating local position was wrong which is funny because when I do it the other way it also breaks but then that leads into the second part. 2. I was inverting data and that was what was keeping the system working for some reason awhile back and even when I tried removing it a bit ago it broke which was really weird. anyways if you change both of them they fix each other so YAY I guess… sorry for the AI usage. it didn’t re write my code for me though I hand fixed my problem which I can proudly say all of the code is all hand typed…. (other then stuff I copy and pasted and or reused from other code I wrote… TRUST ME I NEVER WANT TO REWRITE UI SYSTEMS please unity fix the uitoolkit its bad)
I finally made a git hub repository. I also made a new unity project and moved everything into it to clean everything up for the demo I am starting to work on. I also made Bone Rotation matching but it doesn’t contribute all that match. I should be ready to ship/ Have a demo ready soon
LOTS OF DEBUG UI COMEING YOUR WAY! I have added lots of UI stuff to display internal values such as costs as stuff. I have also added the ability to adjust motion matching settings from in the demo so when I upload it to itch.io all of you can play with the system as much as you want and tell me how bad you broke it. to do wise its a bit more UI (some of it doesn’t work or I want it to look better) and then the data points kinda like the unity gizmo stuff I have been showing you here on star dance. anyways its like 1 AM I should have gone to bed HOURS AGO so I’mma go brush my teeth and work some more tomorrow…. my keyboard is starting to fail :-< Goodnight y’all. or good morning to the folk around the world like in Norway or something
The demo UI has been finished and it works quite well! I have a few more things to add like a looping map so that the Character doesn’t just walk off the end of the world and die plus some more main menu stuff I think too. anyways progress has been fast so far so expect more soon!
OK its fixed now but first test build didn’t go so well (see video) the problem was unity wasn’t saving any of my data running in the background because it wasn’t serialized. I also organized project files plus removing unneeded scripts (I reuse code a lot and sometimes things get replaced and just left there) I also if you can’t tell from the video a dropped frame counter to show matches that where canceled because the current frame was good enough to meet the cost threshold. anyways I think now most of what I have to do it package everything up into… well a package then send it off into the world for you the hack clubbers to mess around with. EDIT: I added a demo for Windows to the github if you want to try it early
Not much changed I made an Itch.io for the demo. I also started adding comments to all of my code yeah its hard to remember what does what but I also got the dataset editor done or almost done. then its onto the matcher and then the trajectory maker then we are done there and then onto a tutorial of how to use it then we are done
I finished all the code comments. I know. WOW 4 HOURS OF CODE COMMENTS yeah but I was also cleaning some other parts of it up and fixing some of the indentations. anyways that’s done now only a few more things till we get to ship! but also a note to other devs like me… please comment your code while writing it because I have gotten in the habit of not doing that and as seeing here by 4 hours logged of code comments PLEASE JUST DO IT WHILE WRITING IT BECUASE THIS SUCKS...
|
1792760004.6095197
|
1793364804.6095197
|
|
16179
|
Robotics RoboLens by @mira_j_maroni | Stardance
|
mira_j_maroni
|
2
|
140.27
|
26
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-07T22:22:22Z", "hours": 124.0, "multiplier": 16.83, "payout": 0, "blessing": "neutral", "text": "What did I make? Since Ship #1, RoboLens grew from one AI scanner into four connected tools. I added a rules checker that reads the actual FRC game manual (2024–2026) alongside your robot photo so it’s grounded in real rules instead of guessing. I built out a full shared battery tracker. I added a guided camera that walks you through lighting/tilt/framing and auto-captures once the angle looks right. And the biggest addition: a Team Stats & Match Center with four tabs (My Team, Stats, Events, Sim) pulling live data from The Blue Alliance and the FRC Events API, including a season-wide “World Rating,” a matchup simulator with win probabilities, and bookmarking teams to get real push notifications before their matches. I also spent a lot of time on stuff that doesn’t show up as a “feature” but makes the app actually usable: a queue + exponential backoff + retry system so the shared free-tier Gemini key doesn’t randomly fail scans under load, a report button that feeds recurring AI mistakes back into future scans, offline detection so scans fail fast instead of hanging, and a new instant-loading splash screen. What was challenging for me? The AI usage limits were the most persistent problem. Everyone shares one free-tier Gemini key, so scans would fail with no clear pattern whenever enough people hit it at once. I fixed it in stages: first a queue limiting in-flight requests, then automatic retries with backoff (and respecting the retry-after header when Gemini gave one), then finally surfacing that retry status to the user (“High demand. Retrying (2/3)…”) instead of just showing a dead spinner. The guided camera and push notifications both turned into whole rabbit holes of browser-specific pain (iOS Safari blocking camera/notification permissions unless triggered by a direct tap, no live frame streaming on web so I had to build a custom JS-interop layer from scratch, and push not working on iOS at all unless the site’s added to the home screen firs)t. My favorite bug story: the Match Center’s Events tab would spin forever with zero errors. I burned time adding timeouts and retries that did nothing before realizing the real issue. The spinner wasn’t stuck waiting on the network, it was stuck because nothing ever tried. What am I the most proud of? Going from a single-purpose AI scanner to four genuinely useful, connected tools without the codebase turning into spaghetti. I’m also proud that scans now degrade gracefully under load instead of just failing, and that notifications work reliably even though I had zero experience with service workers or VAPID keys before this. And honestly, just sticking with it. 21 devlogs and three months of real iteration based on actual user feedback instead of giving up. What should people know so they can test your project? Live app: https://mirage54321.github.io/RoboLens/ You can log in as a guest for team 4388 to browse batteries without needing an account. Push notifications currently work reliably on desktop browsers and Android (iOS Safari requires the site to be added to your home screen as a bookmark). You can test this by logging into -4388 (Yes, a negative. I needed a number that wouldn’t have a real team). The AI scan/rules tools need a robot photo to test against; if you don’t have one handy, any well-lit photo of mechanical/electrical components will still show you how the flow works.", "repo": "https://github.com/mirage54321/RoboLens", "demo": "https://mirage54321.github.io/RoboLens/"}, {"ship_num": 2, "ts": "2026-06-22T18:39:59Z", "hours": 5.0, "multiplier": 5.73, "payout": 26, "blessing": "neutral", "text": "I made an app for specifically robotics teams (although this could be used for other things too). This is still a work in progress because there is so much more I want to add. I did this project so that I could make something with an AI model and I’m really proud of myself because Flutter was really hard to work with!", "repo": "https://github.com/mirage54321/RoboLens", "demo": "https://mirage54321.github.io/RoboLens/"}]...
|
[{"post_id": 65393, "ts": "202 [{"post_id": 65393, "ts": "2026-09-10T14:37:50Z", "hours": 6.5575, "text": "Devlog #23 -> Fixed the camera disconnect crash from the feedback. Turns out the camera plugin doesn’t actually throw an error when the hardware gets yanked mid-session, it just quietly flips an internal hasError flag and keeps going. Which is why the app was crashing somewhere random instead of failing cleanly. Nothing in my code was listening for that flag, so it just kept trying to read frames and capture photos off a dead controller. Fixed it by adding a listener that actually watches for that error state, cleans everything up, and drops the user onto a proper “Camera disconnected, try again” screen instead of freezing or throwing. Also fixed the retry logic after a failed capture so it doesn’t try to restart a stream on a controller that’s already dead. Next up: the cursor. Someone pointed that out, because every button on web just shows the default arrow. Live app: https://mirage54321.github.io/RoboLens/a"}, {"post_id": 64602, "ts": "2026-09-09T03:43:12Z", "hours": 4.7133, "text": "Devlog #22 -> I was looking at the comments people left me from my ship and the first thing I noticed that I should work on is the load-in page. It is definitely kind of plain. To be honest, I used Claude to help a lot with this one. Making the spinning circle was pretty easy but I had some complex ideas I wanted to put to work and I just didn’t know how to do it. It has a huge radar that has the colors pop up and says “scounting __ teams”. Overall, I am so happy about this new design! Let me know what you guys think! I also added a folder for the photos so it looks more organized! The next thing I plan to work on “loop crashes if the camera disconnects” comment. Live app: https://mirage54321.github.io/RoboLens/"}, {"post_id": 63881, "ts": "2026-09-07T21:41:15Z", "hours": 2.065, "text": "Updates!!! Basically, I’ve been checking to see how many users and views I have. I have google analytics on my project to keep an eye on it. Right now I have 52 users and 163 views. Which honestly, I am pretty dang happy about. (1) I haven’t really been advertising (2) I literally thought it was gonna be like 20 views Woop woop! Okay, now on these past few days I have been working on getting as many hours as possible. I don’t have a personal computer so I am really aiming for literally any of the laptops. Now, programatically I have been working on removing the 3 burst messages for iPhone (it wouldn’t compact itself on the Apple homescreen). FYI: 90% of the time for this was spent removing the 3 burst messages for iPhone and writing Ship #2… so you better be ready for Ship 2! View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 63811, "ts": "2026-09-07T19:35:43Z", "hours": 0.7911, "text": "Notifications are working again! Basically, the rebuilding of Render helped fix the issue which is kind of ragebait. I’m getting a little nervous though because I wonder what happens if someone doesn’t reopen the bookmark (because that is how it resets). Would they still get the messages? Will it update automatically? Overall, it works great. I just worry there may be an error that arises (that is a risk with any project honestly). View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 63771, "ts": "2026-09-07T18:47:23Z", "hours": 5.1583, "text": "Devlog #19 -> I know I just committed a bunch the last few days. I guess I realized there is only a few days left and I found how to be way more efficent in getting hours. If I just keep this open and something else on another computer and work with them kinda being different windows then I get a lot more time because the switching of windows in the same computer made me keep forgetting to come back and work on the project (and I’m guessing was cutting the time too). Anywho! The most recent changes I have made is removing the ? mark button. I think I was going for a like “about me” thing but I think it was a little redundant and kind of unneccessary to say the least. I added a report button instead (it’s just in the home page). I got this idea from lovable which was something I worked with during my internship. I highkey have been working so much on this project (you can see by all my hours) that I didn’t want to connect it to the backend. It is just a popout that takes you to an gmail link and you can click send. Maybe if I run out of ideas I can add it as a backend. Now, if I am going to be so honest. I have worked a lot on this project and I want to ship but I also want to just crank out some more hours. I’m extremely proud of this project and I need to get it out to the world so that I can get some users! My issues to work on are: Low MongoDB storage for free account Professionalizing (now just looking back over and seeing if there is anything I wanna fix before putting on chief delphi) View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 63405, "ts": "2026-09-07T07:05:48Z", "hours": 8.2653, "text": "Devlog #18 -> Okay, super short devlog just to explain what I am doing. Basically all my hours are spent on fixing the notifications. It is super annoying right now because I am trying to finalize my project and the notifications randomly stop working. I tried clearing out mongoDB and checking on my cron-job and those seem to be working just fine. I’m not sure what to do. I pushed the hours up (for the practice day so I can check it earlier) and redeployed with Render. I shall see tomorrow if it works. View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 62347, "ts": "2026-09-05T17:45:39Z", "hours": 8.4539, "text": "Devlog #17 -> Part 3, wrapping this one up Last piece was making the retrying actually visible instead of just quietly happening in the background. Before, if a scan was retrying you’d just stare at a spinner with no idea what was going on. Now the loading screen updates to say something like “High demand. Retrying (2/3)…” so people know the app hasn’t frozen, it’s just waiting its turn. Between the queueing, the backoff retries on both ends, and this messaging, this should turn a lot of what used to be “scan failed, try again” moments into just a few extra seconds of waiting instead. Doesn’t make the free tier limit go away entirely, but it’s a big improvement over what I had. Current problems / what’s next: Low MongoDB storage for free account Professionalizing (before putting on Chief Delphi)"}, {"post_id": 61646, "ts": "2026-09-04T17:29:48Z", "hours": 9.4403, "text": "Devlog #16 -> Part 2, continuing from #15 Got retries working on top of the queue from last time. If Gemini comes back with a 429 (rate limited) or 503 (overloaded) instead of an actual answer, the backend now waits and tries again automatically, using exponential backoff (2s, then 4s, then 8s) instead of hammering it again right away, up to a few attempts before it actually gives up and reports failure. It also checks for a retry-after header first and uses that instead of the backoff timer if Gemini actually tells us how long to wait. Then I mirrored the same idea on the Flutter side, so if the backend’s retries still aren’t enough, the app itself will retry the whole request a couple more times before showing the user an error."}, {"post_id": 60875, "ts": "2026-09-03T14:59:25Z", "hours": 12.3994, "text": "Devlog #15 -> Part 1 (still working on this one!) Finally started actually tackling the AI usage limit problem from devlog #2. Since everyone using the app shares the same free-tier Gemini key, scans would just randomly fail with no real pattern whenever enough people were hitting it at once. Started on the backend side first: instead of letting every incoming scan hit Gemini immediately, I added a little queue that only lets 2 requests be in-flight to Gemini at a time, and everything else waits its turn. Next step is getting it to actually retry instead of just failing after the wait, but that’s for the next devlog."}, {"post_id": 60344, "ts": "2026-09-02T16:57:27Z", "hours": 7.4197, "text": "Devlog #14 -> Part 5 of 5 Finally! This final devlog is about the small tweaks I made in the app. I made it have a new photo Small one, just swapped out one of the app’s images for something more current since the old one was from way back in devlog #1 and didn’t really represent what the app looks like anymore. Nothing technical here, just housekeeping. It kinda works offline I say “kinda” because this isn’t a real offline mode where you can browse cached data with no internet, it’s more like the app now knows when it’s offline and fails fast instead of just hanging. Basically, I added a ConnectivityCheck helper that on web reads navigator.onLine (a free, instant, synchronous browser check for whether the device has a network interface up) so if you’re in airplane mode or your wifi is off, the AI scan screens can immediately say “can’t use this when offline” instead of sitting there for a full request timeout before failing. On native platforms there’s no real equivalent without pulling in a whole new plugin like connectivity_plus, so I just made the stub always report “online” there — an actually-offline phone still gets caught by the AI request’s own timeout/retry handling, just without the instant fail I get on web. One thing worth noting: this only checks if the device thinks it has a network connection, not whether that network actually has working internet behind it. So a wifi network that’s connected but dead will still say “online” here. That case still gets caught eventually, just the slower way through the request timeout instead of instantly. Good enough for now though, this was mostly about killing the most common case of someone trying to scan with their phone in airplane mode and just watching the app hang. I changed the load in screen Went back and reworked the loading screen from devlog #5. It’s now a plain CSS screen sitting directly in index.html instead of living inside the Flutter app itself, which means it can actually paint the instant the page loads, before Flutter has even finished downloading and booting up. That was the whole point: the old version technically only appeared once Flutter itself was far enough along to render it, so there was still a blank/white flash before it. Now there’s a little animated wordmark and spinner (the spinner’s colored arc cycles through the same colors as the logo letters) that fades out and removes itself once Flutter fires its first real frame. While I was in there I also fixed something unrelated but annoying: Flutter’s web service worker by default waits to activate a new version until every open instance of the app is fully closed, which for something people add to their home screen basically never happens naturally. So people could keep opening an old cached version of the app for a long time after I’d pushed updates. Added a bit of JS to force any newly installed service worker to skip that wait and take over immediately (reloading the page once it does), so the next time someone opens the app after I ship something, they actually get it. And that’s it, I’m all caught up! Five devlogs in one sitting was a lot, but future updates should go back to being posted as I actually build them instead of in one giant backlog dump. Current problems / what’s next: Low MongoDB storage for free account Releasing the app (onto chief delphi to get feedback)!! Making it ready to release (working on the ? and report button) Future ideas: team tracker, public chat for teams, judging/interview prep tool, callout tool, round robin generator View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 59335, "ts": "2026-09-01T02:11:53Z", "hours": 9.7772, "text": "Devlog #13 -> Part 4 of 5 Continuing from #12: more on the match center, mostly the stuff that actually broke on me. Biggest bug of the bunch: on the Events tab, tapping into an event was supposed to show “scheduled to attend” info and the competing teams list, but both of those sections would just spin forever, no error, nothing. I assumed it was a network timing issue at first and went down a whole rabbit hole adding timeouts and retry logic that did absolutely nothing. Turns out the actual problem was way dumber: when I pushed the EventDetailScreen with Navigator.push, that screen gets placed in the Overlay as a sibling of the tab tree, not a child of it, meaning it’s NOT under my MatchScope anymore. So the second that screen tried to call MatchScope.of(context), it threw an assertion error immediately, before initState even got the chance to call setState and load anything. The spinner wasn’t stuck because of a slow network, it was stuck because the load call never even ran. Fixed it by just re-wrapping the pushed route in its own MatchScope using the same controller instance. Felt very silly once I found it, but also weirdly satisfying to finally understand. Next was the matchup simulator and win probabilities. On the schedule screen I show a rough “Red 45% / Blue 55%” prediction for each match. My first instinct was to use each event’s own OPR for this, but that falls apart for upcoming events since OPR doesn’t exist until a team has actually played a match there. So instead I based predictions on the season-wide World Rating from the Stats tab (average points across every event a team’s played all season), which actually gives predictions even for a event that hasn’t started yet. Also went back and hooked the push notification bell (from devlog #11) directly into the match center’s top bar now that there’s finally a “your team” + “your event” to actually attach a subscription to. There’s a little pulsing glow animation on the bell the first time you land on a screen where you could enable alerts, just so people notice it’s there instead of it blending into the top bar. Took way too many attempts to get the glow animation timing feeling smooth and not epileptic-seizure-inducing, but I think I landed on something decent. Genuinely didn’t expect the 4th app to take this many devlogs but here we are. One more to wrap up some small stuff and then I think I’m caught up! View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 58655, "ts": "2026-08-31T04:34:12Z", "hours": 9.4486, "text": "Devlog #12 -> Part 3 of 5 Okay, actual 4th app time: Team Stats & Match Center (internally I’ve just been calling it “match” in the code, so if you dig around the file names that’s why). I started by mocking everything out before touching any real API, just so I could get the UI feeling right first. Made an intro screen (basically pitching the three features: match alerts, team stats, matchup simulator) and a fake mockTeams list with made-up EPA numbers just so I had something to look at while building. Once the layout felt right, I started swapping the fake data for real stuff from The Blue Alliance’s API, proxied through my backend the same way I already had set up for the FRC Events API (so I’m not spamming TBA directly from a bunch of client apps and to keep my API key private). Architecture-wise, I did something different from my other three apps: instead of passing state down through a million constructor params, I made a MatchDataController (basically one class holding all the team/event/match data + loading state) and wrapped the whole feature in a MatchScope, which is just an InheritedNotifier. So anywhere in the match center I can just call MatchScope.of(context) and get the controller without threading it through every single widget. Probably overkill for how big this feature ended up being, but once I had 4 tabs and a handful of sub-screens all needing the same data, I was really glad I did it this way instead of my usual pass-everything-into-the-constructor approach. Speaking of tabs, I split it into 4: My Team -> your team’s own info/next match Stats -> a season-wide “World Rating” I calculate from aggregating TBA’s OPR data across events (this was its own whole thing, might do a separate devlog on it someday) Events -> browse every FRC event, filter by time or location, see who’s live right now Sim -> the matchup simulator I keep promising Getting match data to actually parse right from TBA’s JSON took a minute (comp levels like qm/qf/sf/f needed their own label logic, predicted_time vs actual_time needed fallback logic since actual_time barely ever gets posted until after the match is already over, that kind of thing) but nothing too crazy, just tedious model-writing. Devlog #13 is basically a straight continuation of this one, mostly bug stories and the simulator, so be ready for that. View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 55760, "ts": "2026-08-27T19:05:53Z", "hours": 4.8611, "text": "Devlog #11 -> Part 2 of 5 Now for the second devlog, I am going to talk about how I got notifications to work. This is the one that ate my week. So the goal: when a match is coming up for a team you bookmarked, get a push notification even if the app/tab isn’t open. That last part is the whole problem, because it means I can’t just pop a normal in-app alert, I actually need real browser push notifications, which meant learning a whole new stack: service workers, VAPID keys, and the Push API. Here’s roughly how it works now ->Subscribe -> user picks a team number + event key, the app asks the browser for notification permission, and if granted it registers a push subscription through the service worker using my VAPID public key. Send to backend -> that subscription (plus team number/event key) gets sent to my Express backend and stored in MongoDB. Actually sending them -> the backend uses the web-push npm package to send the push whenever it detects a match coming up for that team. Getting the Flutter side to talk to any of this was its own adventure, since none of this exists as a nice Dart package, so I had to write a JS interop layer (basically a matchPush object in plain JS that Dart calls into) and wire it up through the same conditional-export trick I used for the guided camera: one file for web, one stub file that just says “unsupported” for native, so the app doesn’t explode on phone builds while I haven’t built native push yet. Problems I ran into (there were many) ->iOS Safari straight up does not support web push at all unless the site has been added to the home screen first. So right now notifications basically only work reliably on desktop browsers and Android, and I have to eventually explain that to iOS users somehow. Permission requests have the same “must be triggered by a direct tap” rule I fought with on the guided camera, so subscribing has to happen from an actual button press, no auto-subscribing on load. Debugging this was rough because half the errors happen silently in the service worker, not in the normal Flutter console, so I was stuck adding console.logs into push.js and checking devtools like a caveman for a while.It works now though! Bookmark a team, subscribe, and you’ll get pinged. Native (phone-installed) push notifications are still a “later” problem since that needs a totally different setup (probably Firebase Cloud Messaging), so for now the stub just returns unsupported on native and I’m keeping my scope to web. Two devlogs about the 4th app itself coming up next, promise. View it here: https://mirage54321.github.io/RoboLens/ P.S. For right now there are 3 notifications that send for iOS I plan to change that though!"}, {"post_id": 54198, "ts": "2026-08-25T02:42:40Z", "hours": 8.6025, "text": "Devlog #10 -> Part 1 of 5 Please don’t hurt me, but I have to post multiple devlogs because I worked on so many things in all this time. It’s a lot of stuff to talk about and I don’t want to write it all into one because it will be super unorganized and hard for me to find in the future. In this first devlog, I am going to talk about how I got bookmarks to work. The idea came from thinking ahead to the match notifier. If someone wants alerts for a match, they need a way to say “these are the teams I care about” without having to retype a team number every single time. So I added a bookmark system using shared_preferences (just storing a list of team numbers locally on the device/browser). You can now star a team from a few places in the app and it’ll save to your bookmarks list, which I’m planning to hook the notifier into so it can just loop through your saved teams instead of asking you every time. This one was honestly pretty painless compared to my usual chaos. The only annoying part was making sure the bookmarked list actually persisted correctly on web (learned my lesson from the MongoDB flutter-web thing, so I tested this one on both platforms before moving on lol). Turns out shared_preferences just works with local storage under the hood on web, so no weird surprises this time. Short devlog, I know, but wait till you see what devlog #11 turned into. View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 49268, "ts": "2026-08-17T23:33:09Z", "hours": 6.1139, "text": "Devlog #9 -> Okay, so getting the report button to work was a little finicky. I wasn’t sure what approach to take: I could make it easy by just sending me the reported things and I could look at them and just change the prompt, or I could make it difficult by just feeding every single reported issue back into the AI model so that the AI tries to not make the same mistake (which might be a problem because someone could report something that was actually right), or make it VERY difficult make it feed back into the AI if a similar issues were coming up again and again. Guess which one I chose? Yup, the third. Basically, I decided to store the reports in MongoDB and look for recurring mistakes instead of letting a single report change the AI. That way, if one person reports something incorrectly, it doesn’t immediately affect everyone else’s scans. If the same type of mistake keeps getting reported, I can investigate it and use those reports to improve the AI.This seems really complex, and it was. It took me 4 hours (it says 6 on the devlog but 2 of them was working on the 4th app). Alright, ready for my fourth widget idea………. a match notifier!! Now don’t get too excited. I have more to tell. So, every FRC kid knows that there’s a website called Nexus that sends messages for when your game is going to start so you can get in queue, and it works really well. There’s also this thing I came across called statbotics, and it calculates many things like EPA, and more (I didn’t really stay on that website for too long). So, what I plan to do is combine them. Hopefully, I’m not getting myself into too much of a challenge. This will most definitely be the hardest thing I have ever done on this app. Wish me luck! P.S. I started making the fourth widget before the report button then switched to finish the report button halfway so if you see my commits in a confusing order that’s why. View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 43541, "ts": "2026-08-08T21:26:06Z", "hours": 0.3608, "text": "Devlog #8 -> Part 2/2 (part 2 of this devlog) I also posted my app onto reddit (r/FRC) and got some feedback. From the feedback, I got the message that an AI scanner can’t be all that reliable/useful without a user-end customization. I totally agree with this. The problem is that I’m not sure if it is worth it to spend time on adding in customization tools for the AI as there is not a lot of return from it. Therefore, I think that right now the AI’s are at the best product they can be as of now (besides adding functionality to the report button). So, my time would be better spent adding in more apps into the app. On the bright side, there were lots of positive reviews about the battery section! I am now going to work on the report button for the UI because I think it will take less time to add than adding a fourth app. In my next post, I will have a confirmed idea for the fourth app! Things to work on: Low MongoDB storage for free account Report button on the UI needs to feed into the AI ⭐ (Going to do this first because it is easier) Find solution for Render (takes a long time to power on and first time you try to do anything it always fails) Adding fourth ‘app’ ⭐⭐ View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 43529, "ts": "2026-08-08T21:02:52Z", "hours": 6.3211, "text": "Devlog #7 -> Part 2/2 (but part 1 of this devlog) Okay. This was very annoying to work on because every single time I would edit the code, I would have to recommit in order to see it on the github pages (because the permissions wouldn’t work on the flutter web-server). So, getting the guided camera working on web brought a whole lot of browser-specific issues that don’t show up on native mobile. First, the app would hang on a black loading screen because permission_handler and frame streaming aren’t supported in browsers. I then fixed that by branching on kIsWeb to skip those native-only calls and let the browser’s own getUserMedia prompt handle camera access instead.Next, iOS Safari blocked the camera request outright because it requires permission calls to fire directly from a user tap with no delay -> solved by gating the whole flow behind an explicit “Enable Camera” button instead of auto-starting on page load. Since camera_web doesn’t support live frame streaming and sensors_plus doesn’t handle iOS’s motion permission prompt, live guidance (tilt, brightness, sharpness) had to be rebuilt from scratch for web: a custom JS-interop layer (WebProbe) draws each video frame onto a hidden canvas to sample brightness/sharpness, and separately requests iOS’s DeviceMotionEvent permission to read tilt.That introduced two more subtle bugs (a Dart generics issue causing a null-cast crash in the interop code, and a timing issue where the element wasn’t in the DOM yet when we went looking for it) both fixed with explicit type arguments and a retry loop. With all of that sorted, the guided camera now works consistently across native and web, with live tilt/lighting/sharpness feedback and autocapture. View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 37868, "ts": "2026-07-30T20:50:16Z", "hours": 4.4522, "text": "Devlog #6 -> Part 1/2 Hey! I know it has been awhile since I worked on my project (adding the guided camera has been taking a lot of will power), but I have a new update. I have added the guided camera to the scan screen (which I will add to the rules screen too after it is finished). The process that the guided camera uses is: Startup -> requests camera permission immediately for phone usage (permission_handler) or waits for a tap on “Enable Camera” first for browser usage. Camera setup -> fetches available cameras (from Flutter’s camera plugin), picks the back-facing one (falls back to the first available camera on the device if no back camera is found), sets resolution to high on phone or medium on browser (since browsers often reject high-res requests), then calls initialize() to start the live preview. Manual fallback for taking photos -> the shutter button is always available to captures the photo; this is the primary method right now. Capture -> stops the frame stream (phone only), takes the picture, reads the bytes, then returns them to the previous screen via Navigator.pop(context, bytes). An issue I ran into was camera permissions on iPhone Safari. It worked fine on my laptop but wouldn’t work on my phone. I eventually figured out that iPhones are notorious for always requiring explicit permission prompts, so I solved it by requiring the camera request to be triggered by a direct user tap. Basically, I added an “Enable Camera” button on web that only calls the camera setup when tapped, since iOS Safari silently blocks getUserMedia (instead of showing an error) if there’s any delay between the tap and the request. Now I am working on the flutter comments (“too dark”, “focus”, etc.) because right now those aren’t working. Once I get the live guidance to work, then I will be able to implement an auto-capture (that way the photo is taken when at a good angle). View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 29717, "ts": "2026-07-16T20:11:47Z", "hours": 4.3139, "text": "Devlog #5 -> My app now has a load in screen! Yay! I figured it was necessary just to make my whole app look more professional. It was pretty easy (took lots of time though) to add and I didn’t fall into any huge difficulties. Only a mini one because I tried to make the animation in the home screen class and it got chaotic. Once it was its own file it was quite easy because then I just needed to fix up the UI. Look here if you want to learn more about how to do animations in your own project (if using Flutter): https://docs.flutter.dev/ui/animations I also added a very tiny change to the error message for logging in. Instead of saying “Team already registered”, it tells you to contact my email if you cannot access your team. I guess I got really nervous that someone would try to access their team, and if they couldn’t, they would go and put another team’s number and so on indefinitely. Right now, I think this is a good way to fix the issue, but I’ll add a way to report it in the app; that way, the issue can be solved quicker, and you won’t have to send an email to someone. I did have the idea to make it so there is identification of some sort for each team; however, that would just make the app complex, and my whole goal was to have a simple solution to problems. So, if this app actually does really well in the FRC world, I will add some sort of verification; that way lost accounts can be found, and it would just give all-around easier access. Next, I am going to focus on my long put off task: photo to video. I am not sure how I am going to do this, if I am going to be so honest. My idea for it came fom the Smile Doctors invisalign app where it opens up your camera and tells you things like “go to better lighting” or “move to the left”. However, I think that will be super complex and difficult to implement so I might have to start a lot smaller. Best of luck to myself, I guess! List of current tasks: Low MongoDB storage for free account Report button on the UI needs to feed into the AI ⭐ Find solution for Render (takes a long time to power on and first time you try to do anything it always fails) Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible) ⭐⭐ View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 28023, "ts": "2026-07-14T00:50:11Z", "hours": 4.6822, "text": "Devlog #4 -> Hey! I have been working on the issues I talked about in my last devlog. So far I have successfully solved one: How I solved “Connenction to MongoDB working for flutter web but not github?” -> The issue was that GitHub Pages is a static hosting service and couldn’t connect directly to MongoDB. Therefore I had to update the application so the GitHub Pages frontend communicates with the Express backend on Render. Basically, I just modified some of the backend configuration like changing (in very very simple terms): fetch(‘/battery’) to something like fetch(‘ https://ridgeboticsapp.onrender.com/battery ’). I also added a ? mark button so that if clicked on it can tell you a little more about the app. Right now, it is just saying some basic things but I plan to show this app to my friends and see what they think I should add to that text in order to help people figure out what is going on if they just found my app. Finally, I fixed an error I wasn’t even aware of: the name finder for each teams batteries. I made it so when you sign up your team, it will try to search up the team name with that FRC number. However, there would be times where I would sign in as my team and it would show up with some random name like “Robo-Knights” or something weird. I think this was related to the Gemini model because I had used it while testing, soincorrect team names could have been stored and reused instead of being verified. To fix this, I connected my backend to the official FRC Events API. Now, the app sends the team’s FRC number to FIRST to retrieve the official team information to then save the verified team name. This prevents teams from accidentally being assigned another team’s name and makes sure the battery data is connected to the correct FRC team. It was never too big of an issue because I could always just edit the names in MongoDB but having it so it says the name right 100% of the time is just much easier for me. Now my new top things to work on are: If team is signed up but you don’t have access to your team Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible) Report button working better to fix the AI’s findings View it here: https://mirage54321.github.io/RoboLens/ P.S. If you are trying things out you can sign in as a guest for 4388 and see my teams batteries"}, {"post_id": 24928, "ts": "2026-07-08T20:05:31Z", "hours": 1.5633, "text": "Devlog #3 -> In the past few days I have been working on adding a battery tracker because I believe that it has the potential to be very helpful to FRC teams. Tracking batteries is always a pain for my team, and other teams I’ve connected with can probably agree with that statement. Batteries can randomly fail by not charging or just full-on dying in two minutes. Although one might assume that keeping track of all this is easy, it truly is not. Having a system to hold records and data, I believe, can be very helpful to teams! I’ve added the third tool to the app: a shared battery tracker where a team can log in with a team number and passcode, add batteries, and mark each one as charging, in use, or available. It also lets you flag a battery as weak or unreliable with a note, so if a battery dies mid-match, the whole team can see that history the next time they’re deciding which one to grab. I even added an option for the AI to recommend which battery to use next based on how long it’s been charged and whether it’s been flagged before. My new challenge for this tool has been working with MongoDB. It stores the data fine, but it wasn’t letting me log in (it kept throwing an error). What confused me more is that when I ran it in Flutter web, it worked just fine. This is what I plan to work on for the next few days. To fix my last problem, which was with the AI saying it had spikes in usage, I changed the message to ask the user to try again because it usually works on the try right after. Right now, this is just a temporary solution until I can find a new AI model. List of things to work on: Low MongoDB storage for free account Report button on the UI needs to feed into the AI Find permanant solution for the AI usage Connenction to MongoDB working for flutter web but not github? Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible) View it here: https://mirage54321.github.io/RoboLens/ Log in as guest for team 4388 if you just want to view it. P.S. My logged time is glitching out a bunch and I would love if someone could help me in the #stardance-help on slack."}, {"post_id": 23707, "ts": "2026-07-06T20:19:01Z", "hours": 9.0781, "text": "Devlog #2-> Now that I’ve finished the foundation of my project, I can keep adding tools! On my last ship, I realized I should keep more documentation. So be ready for more devlogs going forward! To get a little more comfortable with what I’m doing, I decided to add another AI scanning tool that I have been thinking about: a rules checker. The general scanner already looked for physical problems like wiring and cracks, but I wanted something that could actually check a robot against the official FRC rulebook. So I added the actual PDFs of past FRC game manuals (2024, 2025, and 2026) and let the user pick which season’s rules to check against. That PDF gets fed into the AI alongside the photo, so instead of relying on whatever general FRC knowledge the model already has (which could be outdated or made up), it’s reading the exact rulebook for that year while it looks at the image. Getting the PDF into the AI request in the first place took some figuring out. I had to load it from the app’s assets, converting it into a format the API would actually accept, and making sure it got sent alongside the image without breaking anything. Once it worked though, the difference was noticeable (the AI’s answers started actually referencing real rules instead of guesses). The bigger challenge was AI usage limits. Not “too many messages” though. Instead, it was more that other people using the same free model at the same time meant my requests were competing with everyone else’s. Half the time a scan would go through fine, and the other half it just wouldn’t, with no real pattern I could find. To fix this I… haven’t yet, honestly. Still an open problem :(. Hoping to hear from you guys for some ideas! I’m a little nervous because AI kind of has a negative connotation in FRC. To address that issue, I’m planning to make the whole app not centered around AI so I’ll be adding more tools. Excited to keep going though, because I just started programming my new idea: adding a battery tracker! View it here: https://mirage54321.github.io/RoboLens/"}, {"post_id": 13860, "ts": "2026-06-22T18:21:53Z", "hours": 4.63, "text": "Devlog #1-> I’ve finished the foundation of my project! The app currently has a simple UI and one AI tool that takes in a photo and tells you what’s wrong with it — whether it’s frayed wiring, loose screws, cracked/bent frames, or corrosion. Goal of my project: cut down the time it takes to find an issue so an FRC team has more time to actually fix the problem, and flag problems a human might miss during a rushed pit-stop check. Working with Flutter to program the UI wasn’t too bad. I’ve always been used to Java and JavaScript, so although Dart isn’t exactly the same, I picked it up pretty fast. I had the most difficulty implementing the AI. I started with an Ollama model, but it was way too slow — it would time out before it ever finished telling me what it noticed in the photo. I then found out Google lets you use a free Gemini model, so I got an API key for that and added it in. The new problem: every time I published my app as a GitHub Page, the API key would get disabled because it wasn’t safe sitting in client-side code. I finally learned the key needs to live on a backend to actually be secure, so I set one up using Render. I’m so proud of how my project is looking so far! Next up, I plan to add more tools, like a rules scanner."}]...
|
Title: Robotics RoboLens by @mira_j_maroni | Stard Title: Robotics RoboLens by @mira_j_maroni | Stardance
Devlogs:
Devlog #1-> I’ve finished the foundation of my project! The app currently has a simple UI and one AI tool that takes in a photo and tells you what’s wrong with it — whether it’s frayed wiring, loose screws, cracked/bent frames, or corrosion. Goal of my project: cut down the time it takes to find an issue so an FRC team has more time to actually fix the problem, and flag problems a human might miss during a rushed pit-stop check. Working with Flutter to program the UI wasn’t too bad. I’ve always been used to Java and JavaScript, so although Dart isn’t exactly the same, I picked it up pretty fast. I had the most difficulty implementing the AI. I started with an Ollama model, but it was way too slow — it would time out before it ever finished telling me what it noticed in the photo. I then found out Google lets you use a free Gemini model, so I got an API key for that and added it in. The new problem: every time I published my app as a GitHub Page, the API key would get disabled because it wasn’t safe sitting in client-side code. I finally learned the key needs to live on a backend to actually be secure, so I set one up using Render. I’m so proud of how my project is looking so far! Next up, I plan to add more tools, like a rules scanner.
Devlog #2-> Now that I’ve finished the foundation of my project, I can keep adding tools! On my last ship, I realized I should keep more documentation. So be ready for more devlogs going forward! To get a little more comfortable with what I’m doing, I decided to add another AI scanning tool that I have been thinking about: a rules checker. The general scanner already looked for physical problems like wiring and cracks, but I wanted something that could actually check a robot against the official FRC rulebook. So I added the actual PDFs of past FRC game manuals (2024, 2025, and 2026) and let the user pick which season’s rules to check against. That PDF gets fed into the AI alongside the photo, so instead of relying on whatever general FRC knowledge the model already has (which could be outdated or made up), it’s reading the exact rulebook for that year while it looks at the image. Getting the PDF into the AI request in the first place took some figuring out. I had to load it from the app’s assets, converting it into a format the API would actually accept, and making sure it got sent alongside the image without breaking anything. Once it worked though, the difference was noticeable (the AI’s answers started actually referencing real rules instead of guesses). The bigger challenge was AI usage limits. Not “too many messages” though. Instead, it was more that other people using the same free model at the same time meant my requests were competing with everyone else’s. Half the time a scan would go through fine, and the other half it just wouldn’t, with no real pattern I could find. To fix this I… haven’t yet, honestly. Still an open problem :(. Hoping to hear from you guys for some ideas! I’m a little nervous because AI kind of has a negative connotation in FRC. To address that issue, I’m planning to make the whole app not centered around AI so I’ll be adding more tools. Excited to keep going though, because I just started programming my new idea: adding a battery tracker! View it here: https://mirage54321.github.io/RoboLens/
Devlog #3 -> In the past few days I have been working on adding a battery tracker because I believe that it has the potential to be very helpful to FRC teams. Tracking batteries is always a pain for my team, and other teams I’ve connected with can probably agree with that statement. Batteries can randomly fail by not charging or just full-on dying in two minutes. Although one might assume that keeping track of all this is easy, it truly is not. Having a system to hold records and data, I believe, can be very helpful to teams! I’ve added the third tool to the app: a shared battery tracker where a team can log in with a team number and passcode, add batteries, and mark each one as charging, in use, or available. It also lets you flag a battery as weak or unreliable with a note, so if a battery dies mid-match, the whole team can see that history the next time they’re deciding which one to grab. I even added an option for the AI to recommend which battery to use next based on how long it’s been charged and whether it’s been flagged before. My new challenge for this tool has been working with MongoDB. It stores the data fine, but it wasn’t letting me log in (it kept throwing an error). What confused me more is that when I ran it in Flutter web, it worked just fine. This is what I plan to work on for the next few days. To fix my last problem, which was with the AI saying it had spikes in usage, I changed the message to ask the user to try again because it usually works on the try right after. Right now, this is just a temporary solution until I can find a new AI model. List of things to work on: Low MongoDB storage for free account Report button on the UI needs to feed into the AI Find permanant solution for the AI usage Connenction to MongoDB working for flutter web but not github? Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible) View it here: https://mirage54321.github.io/RoboLens/ Log in as guest for team 4388 if you just want to view it. P.S. My logged time is glitching out a bunch and I would love if someone could help me in the #stardance-help on slack.
Devlog #4 -> Hey! I have been working on the issues I talked about in my last devlog. So far I have successfully solved one: How I solved “Connenction to MongoDB working for flutter web but not github?” -> The issue was that GitHub Pages is a static hosting service and couldn’t connect directly to MongoDB. Therefore I had to update the application so the GitHub Pages frontend communicates with the Express backend on Render. Basically, I just modified some of the backend configuration like changing (in very very simple terms): fetch(‘/battery’) to something like fetch(‘ https://ridgeboticsapp.onrender.com/battery ’). I also added a ? mark button so that if clicked on it can tell you a little more about the app. Right now, it is just saying some basic things but I plan to show this app to my friends and see what they think I should add to that text in order to help people figure out what is going on if they just found my app. Finally, I fixed an error I wasn’t even aware of: the name finder for each teams batteries. I made it so when you sign up your team, it will try to search up the team name with that FRC number. However, there would be times where I would sign in as my team and it would show up with some random name like “Robo-Knights” or something weird. I think this was related to the Gemini model because I had used it while testing, soincorrect team names could have been stored and reused instead of being verified. To fix this, I connected my backend to the official FRC Events API. Now, the app sends the team’s FRC number to FIRST to retrieve the official team information to then save the verified team name. This prevents teams from accidentally being assigned another team’s name and makes sure the battery data is connected to the correct FRC team. It was never too big of an issue because I could always just edit the names in MongoDB but having it so it says the name right 100% of the time is just much easier for me. Now my new top things to work on are: If team is signed up but you don’t have access to your team Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible) Report button working better to fix the AI’s findings View it here: https://mirage54321.github.io/RoboLens/ P.S. If you are trying things out you can sign in as a guest for 4388 and see my teams batteries
Devlog #5 -> My app now has a load in screen! Yay! I figured it was necessary just to make my whole app look more professional. It was pretty easy (took lots of time though) to add and I didn’t fall into any huge difficulties. Only a mini one because I tried to make the animation in the home screen class and it got chaotic. Once it was its own file it was quite easy because then I just needed to fix up the UI. Look here if you want to learn more about how to do animations in your own project (if using Flutter): https://docs.flutter.dev/ui/animations I also added a very tiny change to the error message for logging in. Instead of saying “Team already registered”, it tells you to contact my email if you cannot access your team. I guess I got really nervous that someone would try to access their team, and if they couldn’t, they would go and put another team’s number and so on indefinitely. Right now, I think this is a good way to fix the issue, but I’ll add a way to report it in the app; that way, the issue can be solved quicker, and you won’t have to send an email to someone. I did have the idea to make it so there is identification of some sort for each team; however, that would just make the app complex, and my whole goal was to have a simple solution to problems. So, if this app actually does really well in the FRC world, I will add some sort of verification; that way lost accounts can be found, and it would just give all-around easier access. Next, I am going to focus on my long put off task: photo to video. I am not sure how I am going to do this, if I am going to be so honest. My idea for it came fom the Smile Doctors invisalign app where it opens up your camera and tells you things like “go to better lighting” or “move to the left”. However, I think that will be super complex and difficult to implement so I might have to start a lot smaller. Best of luck to myself, I guess! List of current tasks: Low MongoDB storage for free account Report button on the UI needs to feed into the AI ⭐ Find solution for Render (takes a long time to power on and first time you try to do anything it always fails) Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible) ⭐⭐ View it here: https://mirage54321.github.io/RoboLens/
Devlog #6 -> Part 1/2 Hey! I know it has been awhile since I worked on my project (adding the guided camera has been taking a lot of will power), but I have a new update. I have added the guided camera to the scan screen (which I will add to the rules screen too after it is finished). The process that the guided camera uses is: Startup -> requests camera permission immediately for phone usage (permission_handler) or waits for a tap on “Enable Camera” first for browser usage. Camera setup -> fetches available cameras (from Flutter’s camera plugin), picks the back-facing one (falls back to the first available camera on the device if no back camera is found), sets resolution to high on phone or medium on browser (since browsers often reject high-res requests), then calls initialize() to start the live preview. Manual fallback for taking photos -> the shutter button is always available to captures the photo; this is the primary method right now. Capture -> stops the frame stream (phone only), takes the picture, reads the bytes, then returns them to the previous screen via Navigator.pop(context, bytes). An issue I ran into was camera permissions on iPhone Safari. It worked fine on my laptop but wouldn’t work on my phone. I eventually figured out that iPhones are notorious for always requiring explicit permission prompts, so I solved it by requiring the camera request to be triggered by a direct user tap. Basically, I added an “Enable Camera” button on web that only calls the camera setup when tapped, since iOS Safari silently blocks getUserMedia (instead of showing an error) if there’s any delay between the tap and the request. Now I am working on the flutter comments (“too dark”, “focus”, etc.) because right now those aren’t working. Once I get the live guidance to work, then I will be able to implement an auto-capture (that way the photo is taken when at a good angle). View it here: https://mirage54321.github.io/RoboLens/
Devlog #7 -> Part 2/2 (but part 1 of this devlog) Okay. This was very annoying to work on because every single time I would edit the code, I would have to recommit in order to see it on the github pages (because the permissions wouldn’t work on the flutter web-server). So, getting the guided camera working on web brought a whole lot of browser-specific issues that don’t show up on native mobile. First, the app would hang on a black loading screen because permission_handler and frame streaming aren’t supported in browsers. I then fixed that by branching on kIsWeb to skip those native-only calls and let the browser’s own getUserMedia prompt handle camera access instead.Next, iOS Safari blocked the camera request outright because it requires permission calls to fire directly from a user tap with no delay -> solved by gating the whole flow behind an explicit “Enable Camera” button instead of auto-starting on page load. Since camera_web doesn’t support live frame streaming and sensors_plus doesn’t handle iOS’s motion permission prompt, live guidance (tilt, brightness, sharpness) had to be rebuilt from scratch for web: a custom JS-interop layer (WebProbe) draws each video frame onto a hidden canvas to sample brightness/sharpness, and separately requests iOS’s DeviceMotionEvent permission to read tilt.That introduced two more subtle bugs (a Dart generics issue causing a null-cast crash in the interop code, and a timing issue where the element wasn’t in the DOM yet when we went looking for it) both fixed with explicit type arguments and a retry loop. With all of that sorted, the guided camera now works consistently across native and web, with live tilt/lighting/sharpness feedback and autocapture. View it here: https://mirage54321.github.io/RoboLens/
Devlog #8 -> Part 2/2 (part 2 of this devlog) I also posted my app onto reddit (r/FRC) and got some feedback. From the feedback, I got the message that an AI scanner can’t be all that reliable/useful without a user-end customization. I totally agree with this. The problem is that I’m not sure if it is worth it to spend time on adding in customization tools for the AI as there is not a lot of return from it. Therefore, I think that right now the AI’s are at the best product they can be as of now (besides adding functionality to the report button). So, my time would be better spent adding in more apps into the app. On the bright side, there were lots of positive reviews about the battery section! I am now going to work on the report button for the UI because I think it will take less time to add than adding a fourth app. In my next post, I will have a confirmed idea for the fourth app! Things to work on: Low MongoDB storage for free account Report button on the UI needs to feed into the AI ⭐ (Going to do this first because it is easier) Find solution for Render (takes a long time to power on and first time you try to do anything it always fails) Adding fourth ‘app’ ⭐⭐ View it here: https://mirage54321.github.io/RoboLens/
Devlog #9 -> Okay, so getting the report button to work was a little finicky. I wasn’t sure what approach to take: I could make it easy by just sending me the reported things and I could look at them and just change the prompt, or I could make it difficult by just feeding every single reported issue back into the AI model so that the AI tries to not make the same mistake (which might be a problem because someone could report something that was actually right), or make it VERY difficult make it feed back into the AI if a similar issues were coming up again and again. Guess which one I chose? Yup, the third. Basically, I decided to store the reports in MongoDB and look for recurring mistakes instead of letting a single report change the AI. That way, if one person reports something incorrectly, it doesn’t immediately affect everyone else’s scans. If the same type of mistake keeps getting reported, I can investigate it and use those reports to improve the AI.This seems really complex, and it was. It took me 4 hours (it says 6 on the devlog but 2 of them was working on the 4th app). Alright, ready for my fourth widget idea………. a match notifier!! Now don’t get too excited. I have more to tell. So, every FRC kid knows that there’s a website called Nexus that sends messages for when your game is going to start so you can get in queue, and it works really well. There’s also this thing I came across called statbotics, and it calculates many things like EPA, and more (I didn’t really stay on that website for too long). So, what I plan to do is combine them. Hopefully, I’m not getting myself into too much of a challenge. This will most definitely be the hardest thing I have ever done on this app. Wish me luck! P.S. I started making the fourth widget before the report button then switched to finish the report button halfway so if you see my commits in a confusing order that’s why. View it here: https://mirage54321.github.io/RoboLens/
Devlog #10 -> Part 1 of 5 Please don’t hurt me, but I have to post multiple devlogs because I worked on so many things in all this time. It’s a lot of stuff to talk about and I don’t want to write it all into one because it will be super unorganized and hard for me to find in the future. In this first devlog, I am going to talk about how I got bookmarks to work. The idea came from thinking ahead to the match notifier. If someone wants alerts for a match, they need a way to say “these are the teams I care about” without having to retype a team number every single time. So I added a bookmark system using shared_preferences (just storing a list of team numbers locally on the device/browser). You can now star a team from a few places in the app and it’ll save to your bookmarks list, which I’m planning to hook the notifier into so it can just loop through your saved teams instead of asking you every time. This one was honestly pretty painless compared to my usual chaos. The only annoying part was making sure the bookmarked list actually persisted correctly on web (learned my lesson from the MongoDB flutter-web thing, so I tested this one on both platforms before moving on lol). Turns out shared_preferences just works with local storage under the hood on web, so no weird surprises this time. Short devlog, I know, but wait till you see what devlog #11 turned into. View it here: https://mirage54321.github.io/RoboLens/
Devlog #11 -> Part 2 of 5 Now for the second devlog, I am going to talk about how I got notifications to work. This is the one that ate my week. So the goal: when a match is coming up for a team you bookmarked, get a push notification even if the app/tab isn’t open. That last part is the whole problem, because it means I can’t just pop a normal in-app alert, I actually need real browser push notifications, which meant learning a whole new stack: service workers, VAPID keys, and the Push API. Here’s roughly how it works now ->Subscribe -> user picks a team number + event key, the app asks the browser for notification permission, and if granted it registers a push subscription through the service worker using my VAPID public key. Send to backend -> that subscription (plus team number/event key) gets sent to my Express backend and stored in MongoDB. Actually sending them -> the backend uses the web-push npm package to send the push whenever it detects a match coming up for that team. Getting the Flutter side to talk to any of this was its own adventure, since none of this exists as a nice Dart package, so I had to write a JS interop layer (basically a matchPush object in plain JS that Dart calls into) and wire it up through the same conditional-export trick I used for the guided camera: one file for web, one stub file that just says “unsupported” for native, so the app doesn’t explode on phone builds while I haven’t built native push yet. Problems I ran into (there were many) ->iOS Safari straight up does not support web push at all unless the site has been added to the home screen first. So right now notifications basically only work reliably on desktop browsers and Android, and I have to eventually explain that to iOS users somehow. Permission requests have the same “must be triggered by a direct tap” rule I fought with on the guided camera, so subscribing has to happen from an actual button press, no auto-subscribing on load. Debugging this was rough because half the errors happen silently in the service worker, not in the normal Flutter console, so I was stuck adding console.logs into push.js and checking devtools like a caveman for a while.It works now though! Bookmark a team, subscribe, and you’ll get pinged. Native (phone-installed) push notifications are still a “later” problem since that needs a totally different setup (probably Firebase Cloud Messaging), so for now the stub just returns unsupported on native and I’m keeping my scope to web. Two devlogs about the 4th app itself coming up next, promise. View it here: https://mirage54321.github.io/RoboLens/ P.S. For right now there are 3 notifications that send for iOS I plan to change that though!
Devlog #12 -> Part 3 of 5 Okay, actual 4th app time: Team Stats & Match Center (internally I’ve just been calling it “match” in the code, so if you dig around the file names that’s why). I started by mocking everything out before touching any real API, just so I could get the UI feeling right first. Made an intro screen (basically pitching the three features: match alerts, team stats, matchup simulator) and a fake mockTeams list with made-up EPA numbers just so I had something to look at while building. Once the layout felt right, I started swapping the fake data for real stuff from The Blue Alliance’s API, proxied through my backend the same way I already had set up for the FRC Events API (so I’m not spamming TBA directly from a bunch of client apps and to keep my API key private). Architecture-wise, I did something different from my other three apps: instead of passing state down through a million constructor params, I made a MatchDataController (basically one class holding all the team/event/match data + loading state) and wrapped the whole feature in a MatchScope, which is just an InheritedNotifier. So anywhere in the match center I can just call MatchScope.of(context) and get the controller without threading it through every single widget. Probably overkill for how big this feature ended up being, but once I had 4 tabs and a handful of sub-screens all needing the same data, I was really glad I did it this way instead of my usual pass-everything-into-the-constructor approach. Speaking of tabs, I split it into 4: My Team -> your team’s own info/next match Stats -> a season-wide “World Rating” I calculate from aggregating TBA’s OPR data across events (this was its own whole thing, might do a separate devlog on it someday) Events -> browse every FRC event, filter by time or location, see who’s live right now Sim -> the matchup simulator I keep promising Getting match data to actually parse right from TBA’s JSON took a minute (comp levels like qm/qf/sf/f needed their own label logic, predicted_time vs actual_time needed fallback logic since actual_time barely ever gets posted until after the match is already over, that kind of thing) but nothing too crazy, just tedious model-writing. Devlog #13 is basically a straight continuation of this one, mostly bug stories and the simulator, so be ready for that. View it here: https://mirage54321.github.io/RoboLens/
Devlog #13 -> Part 4 of 5 Continuing from #12: more on the match center, mostly the stuff that actually broke on me. Biggest bug of the bunch: on the Events tab, tapping into an event was supposed to show “scheduled to attend” info and the competing teams list, but both of those sections would just spin forever, no error, nothing. I assumed it was a network timing issue at first and went down a whole rabbit hole adding timeouts and retry logic that did absolutely nothing. Turns out the actual problem was way dumber: when I pushed the EventDetailScreen with Navigator.push, that screen gets placed in the Overlay as a sibling of the tab tree, not a child of it, meaning it’s NOT under my MatchScope anymore. So the second that screen tried to call MatchScope.of(context), it threw an assertion error immediately, before initState even got the chance to call setState and load anything. The spinner wasn’t stuck because of a slow network, it was stuck because the load call never even ran. Fixed it by just re-wrapping the pushed route in its own MatchScope using the same controller instance. Felt very silly once I found it, but also weirdly satisfying to finally understand. Next was the matchup simulator and win probabilities. On the schedule screen I show a rough “Red 45% / Blue 55%” prediction for each match. My first instinct was to use each event’s own OPR for this, but that falls apart for upcoming events since OPR doesn’t exist until a team has actually played a match there. So instead I based predictions on the season-wide World Rating from the Stats tab (average points across every event a team’s played all season), which actually gives predictions even for a event that hasn’t started yet. Also went back and hooked the push notification bell (from devlog #11) directly into the match center’s top bar now that there’s finally a “your team” + “your event” to actually attach a subscription to. There’s a little pulsing glow animation on the bell the first time you land on a screen where you could enable alerts, just so people notice it’s there instead of it blending into the top bar. Took way too many attempts to get the glow animation timing feeling smooth and not epileptic-seizure-inducing, but I think I landed on something decent. Genuinely didn’t expect the 4th app to take this many devlogs but here we are. One more to wrap up some small stuff and then I think I’m caught up! View it here: https://mirage54321.github.io/RoboLens/
Devlog #14 -> Part 5 of 5 Finally! This final devlog is about the small tweaks I made in the app. I made it have a new photo Small one, just swapped out one of the app’s images for something more current since the old one was from way back in devlog #1 and didn’t really represent what the app looks like anymore. Nothing technical here, just housekeeping. It kinda works offline I say “kinda” because this isn’t a real offline mode where you can browse cached data with no internet, it’s more like the app now knows when it’s offline and fails fast instead of just hanging. Basically, I added a ConnectivityCheck helper that on web reads navigator.onLine (a free, instant, synchronous browser check for whether the device has a network interface up) so if you’re in airplane mode or your wifi is off, the AI scan screens can immediately say “can’t use this when offline” instead of sitting there for a full request timeout before failing. On native platforms there’s no real equivalent without pulling in a whole new plugin like connectivity_plus, so I just made the stub always report “online” there — an actually-offline phone still gets caught by the AI request’s own timeout/retry handling, just without the instant fail I get on web. One thing worth noting: this only checks if the device thinks it has a network connection, not whether that network actually has working internet behind it. So a wifi network that’s connected but dead will still say “online” here. That case still gets caught eventually, just the slower way through the request timeout instead of instantly. Good enough for now though, this was mostly about killing the most common case of someone trying to scan with their phone in airplane mode and just watching the app hang. I changed the load in screen Went back and reworked the loading screen from devlog #5. It’s now a plain CSS screen sitting directly in index.html instead of living inside the Flutter app itself, which means it can actually paint the instant the page loads, before Flutter has even finished downloading and booting up. That was the whole point: the old version technically only appeared once Flutter itself was far enough along to render it, so there was still a blank/white flash before it. Now there’s a little animated wordmark and spinner (the spinner’s colored arc cycles through the same colors as the logo letters) that fades out and removes itself once Flutter fires its first real frame. While I was in there I also fixed something unrelated but annoying: Flutter’s web service worker by default waits to activate a new version until every open instance of the app is fully closed, which for something people add to their home screen basically never happens naturally. So people could keep opening an old cached version of the app for a long time after I’d pushed updates. Added a bit of JS to force any newly installed service worker to skip that wait and take over immediately (reloading the page once it does), so the next time someone opens the app after I ship something, they actually get it. And that’s it, I’m all caught up! Five devlogs in one sitting was a lot, but future updates should go back to being posted as I actually build them instead of in one giant backlog dump. Current problems / what’s next: Low MongoDB storage for free account Releasing the app (onto chief delphi to get feedback)!! Making it ready to release (working on the ? and report button) Future ideas: team tracker, public chat for teams, judging/interview prep tool, callout tool, round robin generator View it here: https://mirage54321.github.io/RoboLens/
Devlog #15 -> Part 1 (still working on this one!) Finally started actually tackling the AI usage limit problem from devlog #2. Since everyone using the app shares the same free-tier Gemini key, scans would just randomly fail with no real pattern whenever enough people were hitting it at once. Started on the backend side first: instead of letting every incoming scan hit Gemini immediately, I added a little queue that only lets 2 requests be in-flight to Gemini at a time, and everything else waits its turn. Next step is getting it to actually retry instead of just failing after the wait, but that’s for the next devlog.
Devlog #16 -> Part 2, continuing from #15 Got retries working on top of the queue from last time. If Gemini comes back with a 429 (rate limited) or 503 (overloaded) instead of an actual answer, the backend now waits and tries again automatically, using exponential backoff (2s, then 4s, then 8s) instead of hammering it again right away, up to a few attempts before it actually gives up and reports failure. It also checks for a retry-after header first and uses that instead of the backoff timer if Gemini actually tells us how long to wait. Then I mirrored the same idea on the Flutter side, so if the backend’s retries still aren’t enough, the app itself will retry the whole request a couple more times before showing the user an error.
Devlog #17 -> Part 3, wrapping this one up Last piece was making the retrying actually visible instead of just quietly happening in the background. Before, if a scan was retrying you’d just stare at a spinner with no idea what was going on. Now the loading screen updates to say something like “High demand. Retrying (2/3)…” so people know the app hasn’t frozen, it’s just waiting its turn. Between the queueing, the backoff retries on both ends, and this messaging, this should turn a lot of what used to be “scan failed, try again” moments into just a few extra seconds of waiting instead. Doesn’t make the free tier limit go away entirely, but it’s a big improvement over what I had. Current problems / what’s next: Low MongoDB storage for free account Professionalizing (before putting on Chief Delphi)
Devlog #18 -> Okay, super short devlog just to explain what I am doing. Basically all my hours are spent on fixing the notifications. It is super annoying right now because I am trying to finalize my project and the notifications randomly stop working. I tried clearing out mongoDB and checking on my cron-job and those seem to be working just fine. I’m not sure what to do. I pushed the hours up (for the practice day so I can check it earlier) and redeployed with Render. I shall see tomorrow if it works. View it here: https://mirage54321.github.io/RoboLens/
Devlog #19 -> I know I just committed a bunch the last few days. I guess I realized there is only a few days left and I found how to be way more efficent in getting hours. If I just keep this open and something else on another computer and work with them kinda being different windows then I get a lot more time because the switching of windows in the same computer made me keep forgetting to come back and work on the project (and I’m guessing was cutting the time too). Anywho! The most recent changes I have made is removing the ? mark button. I think I was going for a like “about me” thing but I think it was a little redundant and kind of unneccessary to say the least. I added a report button instead (it’s just in the home page). I got this idea from lovable which was something I worked with during my internship. I highkey have been working so much on this project (you can see by all my hours) that I didn’t want to connect it to the backend. It is just a popout that takes you to an gmail link and you can click send. Maybe if I run out of ideas I can add it as a backend. Now, if I am going to be so honest. I have worked a lot on this project and I want to ship but I also want to just crank out some more hours. I’m extremely proud of this project and I need to get it out to the world so that I can get some users! My issues to work on are: Low MongoDB storage for free account Professionalizing (now just looking back over and seeing if there is anything I wanna fix before putting on chief delphi) View it here: https://mirage54321.github.io/RoboLens/
Notifications are working again! Basically, the rebuilding of Render helped fix the issue which is kind of ragebait. I’m getting a little nervous though because I wonder what happens if someone doesn’t reopen the bookmark (because that is how it resets). Would they still get the messages? Will it update automatically? Overall, it works great. I just worry there may be an error that arises (that is a risk with any project honestly). View it here: https://mirage54321.github.io/RoboLens/
Updates!!! Basically, I’ve been checking to see how many users and views I have. I have google analytics on my project to keep an eye on it. Right now I have 52 users and 163 views. Which honestly, I am pretty dang happy about. (1) I haven’t really been advertising (2) I literally thought it was gonna be like 20 views Woop woop! Okay, now on these past few days I have been working on getting as many hours as possible. I don’t have a personal computer so I am really aiming for literally any of the laptops. Now, programatically I have been working on removing the 3 burst messages for iPhone (it wouldn’t compact itself on the Apple homescreen). FYI: 90% of the time for this was spent removing the 3 burst messages for iPhone and writing Ship #2… so you better be ready for Ship 2! View it here: https://mirage54321.github.io/RoboLens/
Devlog #22 -> I was looking at the comments people left me from my ship and the first thing I noticed that I should work on is the load-in page. It is definitely kind of plain. To be honest, I used Claude to help a lot with this one. Making the spinning circle was pretty easy but I had some complex ideas I wanted to put to work and I just didn’t know how to do it. It has a huge radar that has the colors pop up and says “scounting __ teams”. Overall, I am so happy about this new design! Let me know what you guys think! I also added a folder for the photos so it looks more organized! The next thing I plan to work on “loop crashes if the camera disconnects” comment. Live app: https://mirage54321.github.io/RoboLens/
Devlog #23 -> Fixed the camera disconnect crash from the feedback. Turns out the camera plugin doesn’t actually throw an error when the hardware gets yanked mid-session, it just quietly flips an internal hasError flag and keeps going. Which is why the app was crashing somewhere random instead of failing cleanly. Nothing in my code was listening for that flag, so it just kept trying to read frames and capture photos off a dead controller. Fixed it by adding a listener that actually watches for that error state, cleans everything up, and drops the user onto a proper “Camera disconnected, try again” screen instead of freezing or throwing. Also fixed the retry logic after a failed capture so it doesn’t try to restart a stream on a controller that’s already dead. Next up: the cursor. Someone pointed that out, because every button on web just shows the default arrow. Live app: https://mirage54321.github.io/RoboLens/a
Ships:
I made an app for specifically robotics teams (although this could be used for other things too). This is still a work in progress because there is so much more I want to add. I did this project so that I could make something with an AI model and I’m really proud of myself because Flutter was really hard to work with!
What did I make? Since Ship #1, RoboLens grew from one AI scanner into four connected tools. I added a rules checker that reads the actual FRC game manual (2024–2026) alongside your robot photo so it’s grounded in real rules instead of guessing. I built out a full shared battery tracker. I added a guided camera that walks you through lighting/tilt/framing and auto-captures once the angle looks right. And the biggest addition: a Team Stats & Match Center with four tabs (My Team, Stats, Events, Sim) pulling live data from The Blue Alliance and the FRC Events API, including a season-wide “World Rating,” a matchup simulator with win probabilities, and bookmarking teams to get real push notifications before their matches. I also spent a lot of time on stuff that doesn’t show up as a “feature” but makes the app actually usable: a queue + exponential backoff + retry system so the shared free-tier Gemini key doesn’t randomly fail scans under load, a report button that feeds recurring AI mistakes back into future scans, offline detection so scans fail fast instead of hanging, and a new instant-loading splash screen. What was challenging for me? The AI usage limits were the most persistent problem. Everyone shares one free-tier Gemini key, so scans would fail with no clear pattern whenever enough people hit it at once. I fixed it in stages: first a queue limiting in-flight requests, then automatic retries with backoff (and respecting the retry-after header when Gemini gave one), then finally surfacing that retry status to the user (“High demand. Retrying (2/3)…”) instead of just showing a dead spinner. The guided camera and push notifications both turned into whole rabbit holes of browser-specific pain (iOS Safari blocking camera/notification permissions unless triggered by a direct tap, no live frame streaming on web so I had to build a custom JS-interop layer from scratch, and push not working on iOS at all unless the site’s added to the home screen firs)t. My favorite bug story: the Match Center’s Events tab would spin forever with zero errors. I burned time adding timeouts and retries that did nothing before realizing the real issue. The spinner wasn’t stuck waiting on the network, it was stuck because nothing ever tried. What am I the most proud of? Going from a single-purpose AI scanner to four genuinely useful, connected tools without the codebase turning into spaghetti. I’m also proud that scans now degrade gracefully under load instead of just failing, and that notifications work reliably even though I had zero experience with service workers or VAPID keys before this. And honestly, just sticking with it. 21 devlogs and three months of real iteration based on actual user feedback instead of giving up. What should people know so they can test your project? Live app: https://mirage54321.github.io/RoboLens/ You can log in as a guest for team 4388 to browse batteries without needing an account. Push notifications currently work reliably on desktop browsers and Android (iOS Safari requires the site to be added to your home screen as a bookmark). You can test this by logging into -4388 (Yes, a negative. I needed a number that wouldn’t have a real team). The AI scan/rules tools need a robot photo to test against; if you don’t have one handy, any well-lit photo of mechanical/electrical components will still show you how the flow works....
|
1792875207.636436
|
1793739207.636436
|
|
50622
|
Quary by @Veert | Stardance
|
Veert
|
1
|
141.33
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-08-20T12:28:09Z", "hours": 14.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "Working on improving the bot behavior aswell as mastering the gravity effects", "repo": "https://github.com/VrSchool11/Quary", "demo": "https://vrschool11.github.io/Quary/"}]...
|
[{"post_id": 67746, "ts": "202 [{"post_id": 67746, "ts": "2026-09-13T23:58:49Z", "hours": 5.4667, "text": "Rewrote Text and captions to sound more human"}, {"post_id": 66299, "ts": "2026-09-12T01:54:14Z", "hours": 13.8675, "text": "Making the game less ai by rewriting the affected parts"}, {"post_id": 65056, "ts": "2026-09-09T21:55:44Z", "hours": 8.4336, "text": "Adding some new features and improving on gameplay and improving the performance of the game on low-end devices"}, {"post_id": 64787, "ts": "2026-09-09T13:29:53Z", "hours": 4.6419, "text": "Working one some gameplay fixed and fixing some bugs"}, {"post_id": 64540, "ts": "2026-09-09T01:07:14Z", "hours": 1.0333, "text": "Completely Rewrote my code as when i shipped it the reviewer said it was over 30% ai now fixed it and its now below 30% ai"}, {"post_id": 64193, "ts": "2026-09-08T12:50:07Z", "hours": 7.6981, "text": "Fixing General Bugs and Fixing The boss fight"}, {"post_id": 63477, "ts": "2026-09-07T11:09:45Z", "hours": 8.8664, "text": "Working On Fixing The readability of the captions and fixing the tips and the tutorial for the game along with improving the layout of the game"}, {"post_id": 62152, "ts": "2026-09-05T12:26:37Z", "hours": 9.0681, "text": "Guns are in. You take one off the first guard you kill, and firing it wakes the whole zone so you build a silencer out of a sleeve, a spring and an oily rag. The king’s an actual fight now too, he winds up before every move so you can dodge it and one of them rips a limb off you and chucks it on the floor. He used to one-shot you if you’d gone full animal, so that’s gone. Checkpoints on every zone now, plus a pile of daft bugs fixed."}, {"post_id": 61860, "ts": "2026-09-04T23:59:57Z", "hours": 4.2339, "text": "Binned the black hole game after about a day and started Quarry instead. You’re a scientist who got kidnapped for something you built, they took your limbs, and you escape by tearing limbs off whatever you can find. Animal ones jump higher and hit harder, but the guards can see you’re not human anymore, which is basically the whole game. First playable version was rubbish. Every gap was too wide to clear, so you couldn’t actually finish it. Since then I’ve added five zones, a king to fight at the end, guns you loot off dead guards, and a silencer you build out of a sleeve, a spring and an oiled rag. Also spent way too long moving the gun sprite up four pixels because at hip height it looked like something else."}, {"post_id": 61283, "ts": "2026-09-04T04:06:54Z", "hours": 2.9639, "text": "Changed the Game Completely and moved onto another game idea as the other one didn’t seem to be working out heres a sneak peek below"}, {"post_id": 59498, "ts": "2026-09-01T11:27:27Z", "hours": 6.7897, "text": "1 September Today i completely redesigned the UI and also added two new features Different Type of foods A SpeedRun mode On top of that cleaned up the repo and fixed some minor issues here and there"}, {"post_id": 57370, "ts": "2026-08-29T14:27:55Z", "hours": 12.195, "text": "aug 29 completely remade the banner today — scrapped the old one and rebuilt it from scratch so it actually holds together. after that i cleaned up the code, tidying things up and cutting out the rough bits, and did some general improvements across the game while i was in there. more features coming soon."}, {"post_id": 54428, "ts": "2026-08-25T13:07:06Z", "hours": 9.8839, "text": "Fixed the big bug where growing your hole past a certain size would whiteout the entire screen — turned out the camera and the glow effects were both scaling wrong at high mass. While I was in there I added a bunch of new stuff: kill cam replays when you die, comets and binary planets that mess with gravity, a tracker that shows when a rogue hole is hunting you, and permanent achievements on top of the regular quests. Hosts can also now tweak gravity strength, arena size, and bot count before opening a station. Caught and fixed a couple of sneaky bugs along the way too, like eat sounds losing their punch on big absorbs. It’s all live on the GitHub Pages link now."}, {"post_id": 52329, "ts": "2026-08-22T13:13:51Z", "hours": 7.4672, "text": "Turned out if you’d already played a round and went back to the menu, you’d hear every bot in the background eating dust at full volume — the sound code treated “nobody’s watching” as “max volume” instead of silent. Fixed.Also added a pile of new stuff to actually do out there: dash (E key, quick burst forward, costs mass), wormholes that teleport you across the map, random supernova blasts that shove everything outward, a gravity slingshot off planets for a free speed boost, slow mass decay if you’re running away with the lead, a shockwave when you eat something way smaller than you, and a token bounty on whoever’s currently #1."}, {"post_id": 52060, "ts": "2026-08-22T03:27:35Z", "hours": 14.3667, "text": "Spent this session on polish and cleanup. Reworked the UI and CSS myself — set up a color palette with variables so the whole game re-themes from one place, and simplified a lot of the styling. Also fixed a performance bug: the game ran fine in Safari but lagged in Chrome. Turned out canvas shadow-blur is way slower in Chromium, so I cut it off the small stuff (dust, particles) and leaned on the additive glow instead. Runs smooth now. Still fixing the plinko bonus round and cleaning things up. More soon."}, {"post_id": 51642, "ts": "2026-08-21T12:55:44Z", "hours": 4.9736, "text": "Big redesign + feature passRepainted the whole UI — it was reading way too “safe dark-mode template,” so it’s now themed around eclipse observation expeditions instead: brass, khaki, oxide colors, stencil title font, manifest-style panels. Gameplay’s untouched, just the look changed.Also shipped a bunch of stuff that had been on the list:personal best (mass + survival time) on the menusettings: quality toggle, volume slider, minimap on/offkill streak popup when you’re on a rollfree color picker for your holefirst-time tutorial popupauto-lowers quality if your fps tankspower-ups: speed, magnet, shieldtimed matches and last-hole-standing modeteams (red vs blue), no friendly firedaily challenge — same seeded world for everyone each dayFound and fixed two real bugs while testing: the shop wasn’t updating your token count after a purchase, and timed matches were miscalculating rank when multiple people hit the clock at once."}, {"post_id": 51436, "ts": "2026-08-21T04:27:59Z", "hours": 3.7672, "text": "UPDATE — CSS rebuild + progression system. Review sent it back saying my CSS looked too AI-made, so I rebuilt the styling myself and actually learned how it works — set up a color palette with variables and re-themed the whole game in one line to prove it. While I was in there I added a progression system: tokens you earn each run and keep between games, side quests for bonus tokens, a cosmetics shop (colors and orbiting shapes, no pay-to-win), and an end-of-run plinko round that multiplies your run tokens (minimum 1x, so you can never lose them), plus arrow-key/WASD movement and a pause button. Biggest headache was the plinko ball not landing in a slot — the physics were using the display size instead of the canvas’s real size — fixed once I matched them."}, {"post_id": 50987, "ts": "2026-08-20T14:02:44Z", "hours": 1.6131, "text": "UPDATE — Multiplayer is live (online, with room codes) Summary Blackhole.io is now real online multiplayer. Create a room, share the code, and someone on another device joins and you can actually eat each other. Not bots — real people. Breakdown moved the whole game simulation onto a server so it’s authoritative (server decides what’s real, clients can’t cheat) clients now only send their mouse target; the server sends back everyone’s positions create/join a room with a short code, each room is its own isolated game added interpolation so other players move smoothly instead of teleporting between updates bots still fill empty rooms so it’s never a dead lobby deployed the server to Render and the game page to GitHub Pages, wired together over WebSockets What fought me The networking logic wasn’t even the hard part — deploying it was. Getting the GitHub Pages page to actually talk to the Render server took forever. The site kept opening my README instead of the game, the WebSocket wouldn’t connect because it was still pointing at localhost, and it had to be wss:// not ws:// or the browser silently blocked it. Bunch of small things that each looked like the whole thing was broken. Once the client finally connected to the live server and I saw a second black hole move on my screen, that was the moment. Note: Server May Take some time to start up"}, {"post_id": 50934, "ts": "2026-08-20T12:25:40Z", "hours": 6.6444, "text": "Working On fixing the lag caused by the advance gravity effects"}, {"post_id": 50512, "ts": "2026-08-19T18:02:36Z", "hours": 7.405, "text": "Spent today getting Blackhole.io off the ground. You’re a black hole — you eat dust, grow, and start pulling in bigger stuff. Got the core loop working: move toward the mouse, swallow anything smaller, get bigger. Made growth area-based so it doesn’t snowball out of control in ten seconds. The part that ate my time was gravity. I didn’t want stuff to just slide toward me — I wanted it to actually orbit and spiral in. Took a lot of fiddling with the numbers before it felt right instead of floaty. About 7.5 hours in so far. Next up: making the other black holes smarter so it’s not just me alone out there aswell as a multiplayer version so you can play with your friends"}]...
|
Title: Quary by @Veert | Stardance
Devlogs:
Spent Title: Quary by @Veert | Stardance
Devlogs:
Spent today getting Blackhole.io off the ground. You’re a black hole — you eat dust, grow, and start pulling in bigger stuff. Got the core loop working: move toward the mouse, swallow anything smaller, get bigger. Made growth area-based so it doesn’t snowball out of control in ten seconds. The part that ate my time was gravity. I didn’t want stuff to just slide toward me — I wanted it to actually orbit and spiral in. Took a lot of fiddling with the numbers before it felt right instead of floaty. About 7.5 hours in so far. Next up: making the other black holes smarter so it’s not just me alone out there aswell as a multiplayer version so you can play with your friends
Working On fixing the lag caused by the advance gravity effects
UPDATE — Multiplayer is live (online, with room codes) Summary Blackhole.io is now real online multiplayer. Create a room, share the code, and someone on another device joins and you can actually eat each other. Not bots — real people. Breakdown moved the whole game simulation onto a server so it’s authoritative (server decides what’s real, clients can’t cheat) clients now only send their mouse target; the server sends back everyone’s positions create/join a room with a short code, each room is its own isolated game added interpolation so other players move smoothly instead of teleporting between updates bots still fill empty rooms so it’s never a dead lobby deployed the server to Render and the game page to GitHub Pages, wired together over WebSockets What fought me The networking logic wasn’t even the hard part — deploying it was. Getting the GitHub Pages page to actually talk to the Render server took forever. The site kept opening my README instead of the game, the WebSocket wouldn’t connect because it was still pointing at localhost, and it had to be wss:// not ws:// or the browser silently blocked it. Bunch of small things that each looked like the whole thing was broken. Once the client finally connected to the live server and I saw a second black hole move on my screen, that was the moment. Note: Server May Take some time to start up
UPDATE — CSS rebuild + progression system. Review sent it back saying my CSS looked too AI-made, so I rebuilt the styling myself and actually learned how it works — set up a color palette with variables and re-themed the whole game in one line to prove it. While I was in there I added a progression system: tokens you earn each run and keep between games, side quests for bonus tokens, a cosmetics shop (colors and orbiting shapes, no pay-to-win), and an end-of-run plinko round that multiplies your run tokens (minimum 1x, so you can never lose them), plus arrow-key/WASD movement and a pause button. Biggest headache was the plinko ball not landing in a slot — the physics were using the display size instead of the canvas’s real size — fixed once I matched them.
Big redesign + feature passRepainted the whole UI — it was reading way too “safe dark-mode template,” so it’s now themed around eclipse observation expeditions instead: brass, khaki, oxide colors, stencil title font, manifest-style panels. Gameplay’s untouched, just the look changed.Also shipped a bunch of stuff that had been on the list:personal best (mass + survival time) on the menusettings: quality toggle, volume slider, minimap on/offkill streak popup when you’re on a rollfree color picker for your holefirst-time tutorial popupauto-lowers quality if your fps tankspower-ups: speed, magnet, shieldtimed matches and last-hole-standing modeteams (red vs blue), no friendly firedaily challenge — same seeded world for everyone each dayFound and fixed two real bugs while testing: the shop wasn’t updating your token count after a purchase, and timed matches were miscalculating rank when multiple people hit the clock at once.
Spent this session on polish and cleanup. Reworked the UI and CSS myself — set up a color palette with variables so the whole game re-themes from one place, and simplified a lot of the styling. Also fixed a performance bug: the game ran fine in Safari but lagged in Chrome. Turned out canvas shadow-blur is way slower in Chromium, so I cut it off the small stuff (dust, particles) and leaned on the additive glow instead. Runs smooth now. Still fixing the plinko bonus round and cleaning things up. More soon.
Turned out if you’d already played a round and went back to the menu, you’d hear every bot in the background eating dust at full volume — the sound code treated “nobody’s watching” as “max volume” instead of silent. Fixed.Also added a pile of new stuff to actually do out there: dash (E key, quick burst forward, costs mass), wormholes that teleport you across the map, random supernova blasts that shove everything outward, a gravity slingshot off planets for a free speed boost, slow mass decay if you’re running away with the lead, a shockwave when you eat something way smaller than you, and a token bounty on whoever’s currently #1.
Fixed the big bug where growing your hole past a certain size would whiteout the entire screen — turned out the camera and the glow effects were both scaling wrong at high mass. While I was in there I added a bunch of new stuff: kill cam replays when you die, comets and binary planets that mess with gravity, a tracker that shows when a rogue hole is hunting you, and permanent achievements on top of the regular quests. Hosts can also now tweak gravity strength, arena size, and bot count before opening a station. Caught and fixed a couple of sneaky bugs along the way too, like eat sounds losing their punch on big absorbs. It’s all live on the GitHub Pages link now.
aug 29 completely remade the banner today — scrapped the old one and rebuilt it from scratch so it actually holds together. after that i cleaned up the code, tidying things up and cutting out the rough bits, and did some general improvements across the game while i was in there. more features coming soon.
1 September Today i completely redesigned the UI and also added two new features Different Type of foods A SpeedRun mode On top of that cleaned up the repo and fixed some minor issues here and there
Changed the Game Completely and moved onto another game idea as the other one didn’t seem to be working out heres a sneak peek below
Binned the black hole game after about a day and started Quarry instead. You’re a scientist who got kidnapped for something you built, they took your limbs, and you escape by tearing limbs off whatever you can find. Animal ones jump higher and hit harder, but the guards can see you’re not human anymore, which is basically the whole game. First playable version was rubbish. Every gap was too wide to clear, so you couldn’t actually finish it. Since then I’ve added five zones, a king to fight at the end, guns you loot off dead guards, and a silencer you build out of a sleeve, a spring and an oiled rag. Also spent way too long moving the gun sprite up four pixels because at hip height it looked like something else.
Guns are in. You take one off the first guard you kill, and firing it wakes the whole zone so you build a silencer out of a sleeve, a spring and an oily rag. The king’s an actual fight now too, he winds up before every move so you can dodge it and one of them rips a limb off you and chucks it on the floor. He used to one-shot you if you’d gone full animal, so that’s gone. Checkpoints on every zone now, plus a pile of daft bugs fixed.
Working On Fixing The readability of the captions and fixing the tips and the tutorial for the game along with improving the layout of the game
Fixing General Bugs and Fixing The boss fight
Completely Rewrote my code as when i shipped it the reviewer said it was over 30% ai now fixed it and its now below 30% ai
Working one some gameplay fixed and fixing some bugs
Adding some new features and improving on gameplay and improving the performance of the game on low-end devices
Making the game less ai by rewriting the affected parts
Rewrote Text and captions to sound more human
Ships:
Working on improving the bot behavior aswell as mastering the gravity effects...
|
1792843006.5874753
|
1792929406.5874753
|
|
16233
|
See-TreePiO by @Aryana | Stardance
|
Aryana
|
1
|
142.0
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-18T06:32:43Z", "hours": 142.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "I made a project that can detect tree/plant species and temperature to determine whether it is dead or not. It displays the result on an LCD in the back, and will start the process of scanning a tree/plant once a button is pressed. The wiring is enclosed in a 3D printed prototype. The most difficult thing I did in this project was likely trying to fit it all in the enclosure. There were a lot of wires here and there, and the button was also quite difficult to fit in, as I needed to solder it on. However, this project was really fun to do, as it helped me gain knowledge about the environment around me, and I got to practice my software and hardware skills. I am really proud of myself, as I completed this project from start to finish, and didn’t give up. To test my project, all you have to do is plug in the battery pack, wait for a minute or so, point the prototype at the tree you want to scan, press the button, and wait for the results. Sometimes, it will take a little longer to get the results, resulting from a difficulty figuring out either the temperature or species of that particular plant/tree.", "repo": "https://github.com/dawinner395/tree-health-scanner", "demo": ""}]...
|
[{"post_id": 69981, "ts": "202 [{"post_id": 69981, "ts": "2026-09-18T05:40:56Z", "hours": 0.35, "text": "I completed the prototype by combining the enclosure with the prototype. I encased the sensors and the LCD. The prototype is finally complete!"}, {"post_id": 69965, "ts": "2026-09-18T04:18:13Z", "hours": 1.5167, "text": "I combined the parts in the bottom half of the prototype, and fit it in the encolsure."}, {"post_id": 67575, "ts": "2026-09-13T18:30:05Z", "hours": 1.3167, "text": "I researched the different types of prevalent trees in California and their temperatures during different seasons. I plan to add this to my database, as I hadn’t previously done this. I used many websites, but Our City Forest was the most helpful."}, {"post_id": 66887, "ts": "2026-09-12T20:45:05Z", "hours": 0.9333, "text": "I fixed the issues that the prototype was facing. I also added a button. The main thing I did was make the prototype able to function without the computer nearby, which can make it so that anyone in the world can use it with a battery pack."}, {"post_id": 64447, "ts": "2026-09-08T20:39:44Z", "hours": 12.0014, "text": "I 3D printed the top half of the housing for the prototype. Next, I will print the bottom half."}, {"post_id": 48288, "ts": "2026-08-16T16:26:44Z", "hours": 10.6833, "text": "I build a CAD of the structure that will house the prototype. I left holes for all the different parts, and will print it later today."}, {"post_id": 36195, "ts": "2026-07-28T03:59:06Z", "hours": 1.7667, "text": "I connected the database to the Raspberry Pi, and tested it out using a house plant. It worked really well, and the next step is to test it outside with a real tree."}, {"post_id": 35375, "ts": "2026-07-26T18:41:48Z", "hours": 1.8333, "text": "I researched about how to use the specific PlantNet API database with the Raspberry Pi and hook that database to a Machine Learning algorthim. I am finally ready to actually make the Machine Learning algorithm and get my hands dirty now that I have the information I need to continue on with the project."}, {"post_id": 32105, "ts": "2026-07-21T01:22:30Z", "hours": 0.3333, "text": "I researched more about the tree datasets, and I found one that has plenty of accurate information: PlantNet API."}, {"post_id": 31560, "ts": "2026-07-20T02:06:06Z", "hours": 0.6333, "text": "I researched the different datasets I can use for the project, as well as the amount of tree data each have in them. I still need to do some more research, but as of now, creating a dataset is the best option if I want to get a wide, accurate range of data. I haven’t found any datasets that match the amount of data I can collect by myself in my neighborhood."}, {"post_id": 29878, "ts": "2026-07-17T01:24:55Z", "hours": 0.6, "text": "I tested the camera and battery pack that came. They both worked really well. Now, I can start inputting the ML algrothm."}, {"post_id": 28665, "ts": "2026-07-15T01:45:42Z", "hours": 3.7747, "text": "I fiinshed the Machine Learning book."}, {"post_id": 28079, "ts": "2026-07-14T03:24:59Z", "hours": 1.0333, "text": "I read Chapters 14 and 15 of the Machine Learning book. The parts will come tomorrow."}, {"post_id": 27961, "ts": "2026-07-13T22:13:23Z", "hours": 0.6833, "text": "I read Chapter 13 of the Machine Learning book."}, {"post_id": 27279, "ts": "2026-07-12T18:33:41Z", "hours": 1.65, "text": "I planned the way I am going to connect the ArduCam to the Raspberry Pi with the thermal sensor. I realized I might have to solder some wires together so that more than one wire can fit on one pin on the Pi. I planned the way the wires are going to connect to the Pi and how I am going to integrate ML into the Pi using the camera and thermal sensor."}, {"post_id": 26242, "ts": "2026-07-10T22:09:55Z", "hours": 1.7667, "text": "I researched more about using the ArduCam with the Raspberry Pi 5. I found troubleshooting techniques and what I need to do after I get the camera (the setup)."}, {"post_id": 25652, "ts": "2026-07-10T02:04:39Z", "hours": 1.6847, "text": "I read Chapters 11 and 12 of the Machine Learning book."}, {"post_id": 25543, "ts": "2026-07-09T21:16:12Z", "hours": 1.5167, "text": "I watched a video about using the ArduCam with the Raspberry Pi 5."}, {"post_id": 25481, "ts": "2026-07-09T19:38:18Z", "hours": 1.2369, "text": "I read Chapters 9 and 10 of the Machine Learning book."}, {"post_id": 25077, "ts": "2026-07-09T01:30:27Z", "hours": 0.4, "text": "I read Chapter 8 of the Machine Learning book. I will stop watching the AI on the Edge videos because they are not accessible on YouTube after Lesson 31. The parts will come day after tomorrow, so I plan to finish reading this book by then."}, {"post_id": 24895, "ts": "2026-07-08T19:02:48Z", "hours": 2.1167, "text": "I watched Lesson 31 of the AI on the Edge videos."}, {"post_id": 24517, "ts": "2026-07-08T03:43:40Z", "hours": 2.1914, "text": "I watched Lesson 30 of the AI on the Edge videos."}, {"post_id": 24471, "ts": "2026-07-08T01:31:47Z", "hours": 1.9167, "text": "I watched Lesson 29 of the AI on the Edge videos."}, {"post_id": 24422, "ts": "2026-07-07T23:28:04Z", "hours": 3.2, "text": "I watched Lesson 28 of the AI on the Edge videos."}, {"post_id": 24308, "ts": "2026-07-07T19:58:17Z", "hours": 2.1003, "text": "I watched Lesson 27 of the AI on the Edge videos."}, {"post_id": 23795, "ts": "2026-07-06T23:03:10Z", "hours": 2.7667, "text": "I watched Lesson 26 of the AI on the Edge videos."}, {"post_id": 23280, "ts": "2026-07-06T04:18:00Z", "hours": 2.3625, "text": "I watched Lesson 25 of the AI on the Edge videos."}, {"post_id": 23236, "ts": "2026-07-06T01:55:59Z", "hours": 3.1667, "text": "I watched Lesson 24 of the AI on the Edge videos."}, {"post_id": 23150, "ts": "2026-07-05T22:46:20Z", "hours": 0.9167, "text": "I read chapters 6 and 7 of the Machine Learning book. I’m just reading and watching the videos until the parts arrive in three or four days. Then, I will work on the technical aspect of the prototype. I have some free time now, so I thought I would learn about Machine Learning and using the Raspberry Pi in the time I have instead of wasting it."}, {"post_id": 22461, "ts": "2026-07-04T22:37:08Z", "hours": 2.0494, "text": "I watched Lesson 23 of the AI on the Edge videos."}, {"post_id": 22380, "ts": "2026-07-04T20:34:29Z", "hours": 3.2, "text": "I watched Lesson 22 of the AI on the Edge videos."}, {"post_id": 21779, "ts": "2026-07-03T21:41:25Z", "hours": 1.5, "text": "I watched Lesson 21 of the AI on the Edge videos."}, {"post_id": 21716, "ts": "2026-07-03T20:11:28Z", "hours": 1.9, "text": "I watched Lesson 20 of the AI on the Edge videos."}, {"post_id": 21285, "ts": "2026-07-03T04:02:15Z", "hours": 2.8383, "text": "I watched Lesson 19 of the AI on the Edge videos."}, {"post_id": 21215, "ts": "2026-07-03T01:11:38Z", "hours": 2.3, "text": "I watched Lesson 18 of the AI on the Edge videos."}, {"post_id": 21101, "ts": "2026-07-02T21:55:09Z", "hours": 3.7, "text": "I watched Lesson 17 of the AI on the Edge videos."}, {"post_id": 20495, "ts": "2026-07-02T03:07:09Z", "hours": 1.9072, "text": "I watched Lesson 16 of the AI on the Edge videos."}, {"post_id": 20442, "ts": "2026-07-02T01:11:45Z", "hours": 2.3333, "text": "I watched Lesson 15 of the AI on the Edge videos."}, {"post_id": 20318, "ts": "2026-07-01T21:43:50Z", "hours": 3.6667, "text": "I watched Lesson 14 of the AI on the Edge videos."}, {"post_id": 19770, "ts": "2026-07-01T04:50:30Z", "hours": 2.7167, "text": "I watched Lesson 13 of the AI on the Edge videos."}, {"post_id": 19679, "ts": "2026-07-01T02:07:59Z", "hours": 1.4167, "text": "I watched Lesson 12 of the AI on the Edge videos."}, {"post_id": 19634, "ts": "2026-07-01T00:35:15Z", "hours": 1.6167, "text": "I read chapters 5 and 6 of the Machine Learning book."}, {"post_id": 19586, "ts": "2026-06-30T22:40:26Z", "hours": 1.6167, "text": "I watched Lesson 11 of the AI on the Edge videos."}, {"post_id": 19523, "ts": "2026-06-30T21:02:23Z", "hours": 1.0494, "text": "I watched Lesson 10 of the AI on the Edge videos."}, {"post_id": 19468, "ts": "2026-06-30T19:58:25Z", "hours": 2.1, "text": "I watched Lesson 9 of the AI on the Edge videos."}, {"post_id": 19018, "ts": "2026-06-30T05:48:49Z", "hours": 2.7, "text": "I watched Lesson 8 of the AI on the Edge videos."}, {"post_id": 18914, "ts": "2026-06-30T03:06:50Z", "hours": 0.75, "text": "I finished reading Chapter 4 of the Machine Learning book. I am also taking more notes on the book now."}, {"post_id": 18878, "ts": "2026-06-30T02:18:01Z", "hours": 0.9, "text": "I sent an email to Ms. Mint (the one I met previosuly) asking if we could meet again for some feedback on my project idea. I also converted my paper plan onto a document and calendar so I can keep track of any deadlines I might have for the project."}, {"post_id": 18760, "ts": "2026-06-29T23:18:51Z", "hours": 4.0, "text": "I watched Lesson 7 of the AI on the Edge videos."}, {"post_id": 17910, "ts": "2026-06-28T20:55:18Z", "hours": 1.2, "text": "I watched Lesson 6 of the AI on the Edge videos."}, {"post_id": 17840, "ts": "2026-06-28T19:40:59Z", "hours": 1.0667, "text": "I watched Lesson 5 of AI on the Edge. In the previous DevLog about the AI on the Edge videos, I actually watched Lesson 4, not Lesson 3."}, {"post_id": 17786, "ts": "2026-06-28T18:35:28Z", "hours": 1.7667, "text": "I made a plan on how to spend my time during this summer, as a lot of time will be dedicated to the ML aspect of my project."}, {"post_id": 17345, "ts": "2026-06-28T02:58:04Z", "hours": 2.0833, "text": "I watched Lesson 3 of the AI on the Edge videos."}, {"post_id": 17217, "ts": "2026-06-27T22:37:39Z", "hours": 3.05, "text": "I finished the 3rd lesson of the AI on the Edge."}, {"post_id": 17106, "ts": "2026-06-27T19:33:57Z", "hours": 1.5667, "text": "I watched the Lesson 2 of the AI on the Edge Raspberry Pi videos."}, {"post_id": 16603, "ts": "2026-06-27T00:34:57Z", "hours": 1.5, "text": "I watched a video about using the Raspberry Pi 5 with ML. Though the Machine Learning book is helpful (and I will keep reading it), I felt that it was not enough to help me with this specific project."}, {"post_id": 16000, "ts": "2026-06-25T23:50:46Z", "hours": 0.9333, "text": "I researched exactly what camera and battery pack to buy (to power the Raspberry Pi). I added them to the cart and will buy it as soon as possible to further develop the prototype. I can’t wait to see how it works with the thermal sensor!"}, {"post_id": 15917, "ts": "2026-06-25T21:22:07Z", "hours": 1.0667, "text": "I just researched about how to combine a thermal sensor with a camera. I found a helpful video and took notes on that video. I also found the specific type of camera I can use, which is a standard Raspberry Pi Camera Module, as well as the connector."}, {"post_id": 15877, "ts": "2026-06-25T20:08:12Z", "hours": 0.6167, "text": "Instead of physically testing the prototypes out (because my dog accidentally stepped on the ultrasonic sensor prototype), I just researched a bit more about the two sensors. I researched the benefits and downsides of each sensor, as well as how accurae they are at specifcally detecting dead trees. I found that people have used ultrasonic sensors before to detect dead trees, but no one has used thermal sensors in the way that I am before to do so. Therefore, I will stick with using a thermal sensor for this prototype. I will have to do more research about the actual pairing between a thermal sensor, a camera, and ML, but the book will help me a lot in the coding of the ML portion."}, {"post_id": 15231, "ts": "2026-06-24T18:49:08Z", "hours": 0.5333, "text": "I compared the 2 prototypes to see which sensor is more accurate (with research, not actual testing). I found that the thermal sensor is more accurate, especially when paired with a camera and an ML algorithm. However, the pairing will be new to me because of the Raspberry Pi. I am more used to working with Aruinos, but through research and practice, I will be able to implement ML. Also, the book I am reading is going to help increase my knowledge on ML. In a future DevLog, I might just test this research just to be sure the thermal sensor is the right choice."}, {"post_id": 14069, "ts": "2026-06-23T01:29:08Z", "hours": 2.3667, "text": "I created the new prototype using the ultrasonic sensor. I coded it, and it successfully functions properly. However, when I tested it with a tree, it didn’t detect it correctly (it said it was dead, not healthy). When I tested it a few more times, it showed accurate readings. I still need to compare the two prototypes together."}, {"post_id": 13982, "ts": "2026-06-22T23:01:27Z", "hours": 0.7219, "text": "I started taking notes on how to work with a thermal sensor, a camera, and ML. I found that it might be hard to integrate all 3 of these things, so I started looking in a different path. I found that I could use ultrasonic distance sensors to detect the unique ultrasonic waves emitted from trees. Therefore, I will start creating a prototype using the ultrasonic sensor and compare the 2 prototypes together."}, {"post_id": 13915, "ts": "2026-06-22T20:24:35Z", "hours": 1.6833, "text": "I copied the code from Command Prompt to GitHub so I have a way to store it somewhere safe. I also double-checkked that the code was correct, and fixed any errors."}, {"post_id": 13555, "ts": "2026-06-22T06:01:12Z", "hours": 3.0333, "text": "I interviewed a tree expert, Ms. Mint, who works at a local farm. She brought up some interesting points, and helped me narrow down what sensors I want to use for my project. She said that using a thermal sensor would be a good idea becuase of its accuracy and easy use. However, when I interviewed her, I had to record the video on my phone instead of on this Hackatime. Therefore, I screen recorded the video and that is the video shown here on this Devlog. I was also with a few other people interested in Ms. Mint’s work, so it wasn’t a one-on-one session, more of a broader overview with a lot of Q-and-A. Overall, this experience was really nice because I got to meet a curator of a nature center, feel the nature all around me, see the difference between dead and healthy trees, and get feedback on my idea."}, {"post_id": 13494, "ts": "2026-06-22T02:51:43Z", "hours": 1.1878, "text": "I wired the thermal sensor, the LCD, the Raspberry Pi, and the LCD backpack. At first, the wiring was not working, so I switched to planning the wiring on paper. Once I finished that, I used a soldering iron to combine two wires to fit in one slot. This is the completed wiring."}, {"post_id": 13467, "ts": "2026-06-22T01:34:43Z", "hours": 0.4833, "text": "I continued reading the Machine Learning book and got to Chapter 4. The book is really interesting and I already feel like I have some knowledge (not a lot, but some) about ML."}, {"post_id": 13450, "ts": "2026-06-22T01:01:41Z", "hours": 2.55, "text": "I continued reading the Machine Learning book and got to Chapter 4. The book is really interesting and I already feel like I have some knowledge (not a lot, but some) about ML."}, {"post_id": 13342, "ts": "2026-06-21T21:15:14Z", "hours": 0.4833, "text": "I have started reading a book about Machine Learning, its history, and how to train/use it to the best of my ability. I have been taking some notes and answering some questions presented in the book. I have gotten through the first 2 chapters so far and plan to finish the book in the next few weeks. This book will really help me use ML in my project, as well as use it in other projects that I may decide to pursue."}, {"post_id": 12793, "ts": "2026-06-20T22:25:53Z", "hours": 0.8169, "text": "I created the code for the basic part of the prototype. The code makes the prototype able to detect the thermal signature of a Redwood Tree and determine whether it is dead or healthy. In the future, I wil re-make the code to incorporate a camera and a Machine Learning algorithm that can be used for more accurate and broader readings, not just for Redwood trees."}, {"post_id": 12756, "ts": "2026-06-20T21:19:55Z", "hours": 1.4667, "text": "Researched about the different techniques and sensors I could use for this project. I found that using a thermal sensor with Machine Learning algorithms and a camera would be the best for my project."}, {"post_id": 12708, "ts": "2026-06-20T19:07:36Z", "hours": 0.6164, "text": "Testing and editing the code and the wiring of the prototype"}]...
|
Title: See-TreePiO by @Aryana | Stardance
Devlogs Title: See-TreePiO by @Aryana | Stardance
Devlogs:
Testing and editing the code and the wiring of the prototype
Researched about the different techniques and sensors I could use for this project. I found that using a thermal sensor with Machine Learning algorithms and a camera would be the best for my project.
I created the code for the basic part of the prototype. The code makes the prototype able to detect the thermal signature of a Redwood Tree and determine whether it is dead or healthy. In the future, I wil re-make the code to incorporate a camera and a Machine Learning algorithm that can be used for more accurate and broader readings, not just for Redwood trees.
I have started reading a book about Machine Learning, its history, and how to train/use it to the best of my ability. I have been taking some notes and answering some questions presented in the book. I have gotten through the first 2 chapters so far and plan to finish the book in the next few weeks. This book will really help me use ML in my project, as well as use it in other projects that I may decide to pursue.
I continued reading the Machine Learning book and got to Chapter 4. The book is really interesting and I already feel like I have some knowledge (not a lot, but some) about ML.
I continued reading the Machine Learning book and got to Chapter 4. The book is really interesting and I already feel like I have some knowledge (not a lot, but some) about ML.
I wired the thermal sensor, the LCD, the Raspberry Pi, and the LCD backpack. At first, the wiring was not working, so I switched to planning the wiring on paper. Once I finished that, I used a soldering iron to combine two wires to fit in one slot. This is the completed wiring.
I interviewed a tree expert, Ms. Mint, who works at a local farm. She brought up some interesting points, and helped me narrow down what sensors I want to use for my project. She said that using a thermal sensor would be a good idea becuase of its accuracy and easy use. However, when I interviewed her, I had to record the video on my phone instead of on this Hackatime. Therefore, I screen recorded the video and that is the video shown here on this Devlog. I was also with a few other people interested in Ms. Mint’s work, so it wasn’t a one-on-one session, more of a broader overview with a lot of Q-and-A. Overall, this experience was really nice because I got to meet a curator of a nature center, feel the nature all around me, see the difference between dead and healthy trees, and get feedback on my idea.
I copied the code from Command Prompt to GitHub so I have a way to store it somewhere safe. I also double-checkked that the code was correct, and fixed any errors.
I started taking notes on how to work with a thermal sensor, a camera, and ML. I found that it might be hard to integrate all 3 of these things, so I started looking in a different path. I found that I could use ultrasonic distance sensors to detect the unique ultrasonic waves emitted from trees. Therefore, I will start creating a prototype using the ultrasonic sensor and compare the 2 prototypes together.
I created the new prototype using the ultrasonic sensor. I coded it, and it successfully functions properly. However, when I tested it with a tree, it didn’t detect it correctly (it said it was dead, not healthy). When I tested it a few more times, it showed accurate readings. I still need to compare the two prototypes together.
I compared the 2 prototypes to see which sensor is more accurate (with research, not actual testing). I found that the thermal sensor is more accurate, especially when paired with a camera and an ML algorithm. However, the pairing will be new to me because of the Raspberry Pi. I am more used to working with Aruinos, but through research and practice, I will be able to implement ML. Also, the book I am reading is going to help increase my knowledge on ML. In a future DevLog, I might just test this research just to be sure the thermal sensor is the right choice.
Instead of physically testing the prototypes out (because my dog accidentally stepped on the ultrasonic sensor prototype), I just researched a bit more about the two sensors. I researched the benefits and downsides of each sensor, as well as how accurae they are at specifcally detecting dead trees. I found that people have used ultrasonic sensors before to detect dead trees, but no one has used thermal sensors in the way that I am before to do so. Therefore, I will stick with using a thermal sensor for this prototype. I will have to do more research about the actual pairing between a thermal sensor, a camera, and ML, but the book will help me a lot in the coding of the ML portion.
I just researched about how to combine a thermal sensor with a camera. I found a helpful video and took notes on that video. I also found the specific type of camera I can use, which is a standard Raspberry Pi Camera Module, as well as the connector.
I researched exactly what camera and battery pack to buy (to power the Raspberry Pi). I added them to the cart and will buy it as soon as possible to further develop the prototype. I can’t wait to see how it works with the thermal sensor!
I watched a video about using the Raspberry Pi 5 with ML. Though the Machine Learning book is helpful (and I will keep reading it), I felt that it was not enough to help me with this specific project.
I watched the Lesson 2 of the AI on the Edge Raspberry Pi videos.
I finished the 3rd lesson of the AI on the Edge.
I watched Lesson 3 of the AI on the Edge videos.
I made a plan on how to spend my time during this summer, as a lot of time will be dedicated to the ML aspect of my project.
I watched Lesson 5 of AI on the Edge. In the previous DevLog about the AI on the Edge videos, I actually watched Lesson 4, not Lesson 3.
I watched Lesson 6 of the AI on the Edge videos.
I watched Lesson 7 of the AI on the Edge videos.
I sent an email to Ms. Mint (the one I met previosuly) asking if we could meet again for some feedback on my project idea. I also converted my paper plan onto a document and calendar so I can keep track of any deadlines I might have for the project.
I finished reading Chapter 4 of the Machine Learning book. I am also taking more notes on the book now.
I watched Lesson 8 of the AI on the Edge videos.
I watched Lesson 9 of the AI on the Edge videos.
I watched Lesson 10 of the AI on the Edge videos.
I watched Lesson 11 of the AI on the Edge videos.
I read chapters 5 and 6 of the Machine Learning book.
I watched Lesson 12 of the AI on the Edge videos.
I watched Lesson 13 of the AI on the Edge videos.
I watched Lesson 14 of the AI on the Edge videos.
I watched Lesson 15 of the AI on the Edge videos.
I watched Lesson 16 of the AI on the Edge videos.
I watched Lesson 17 of the AI on the Edge videos.
I watched Lesson 18 of the AI on the Edge videos.
I watched Lesson 19 of the AI on the Edge videos.
I watched Lesson 20 of the AI on the Edge videos.
I watched Lesson 21 of the AI on the Edge videos.
I watched Lesson 22 of the AI on the Edge videos.
I watched Lesson 23 of the AI on the Edge videos.
I read chapters 6 and 7 of the Machine Learning book. I’m just reading and watching the videos until the parts arrive in three or four days. Then, I will work on the technical aspect of the prototype. I have some free time now, so I thought I would learn about Machine Learning and using the Raspberry Pi in the time I have instead of wasting it.
I watched Lesson 24 of the AI on the Edge videos.
I watched Lesson 25 of the AI on the Edge videos.
I watched Lesson 26 of the AI on the Edge videos.
I watched Lesson 27 of the AI on the Edge videos.
I watched Lesson 28 of the AI on the Edge videos.
I watched Lesson 29 of the AI on the Edge videos.
I watched Lesson 30 of the AI on the Edge videos.
I watched Lesson 31 of the AI on the Edge videos.
I read Chapter 8 of the Machine Learning book. I will stop watching the AI on the Edge videos because they are not accessible on YouTube after Lesson 31. The parts will come day after tomorrow, so I plan to finish reading this book by then.
I read Chapters 9 and 10 of the Machine Learning book.
I watched a video about using the ArduCam with the Raspberry Pi 5.
I read Chapters 11 and 12 of the Machine Learning book.
I researched more about using the ArduCam with the Raspberry Pi 5. I found troubleshooting techniques and what I need to do after I get the camera (the setup).
I planned the way I am going to connect the ArduCam to the Raspberry Pi with the thermal sensor. I realized I might have to solder some wires together so that more than one wire can fit on one pin on the Pi. I planned the way the wires are going to connect to the Pi and how I am going to integrate ML into the Pi using the camera and thermal sensor.
I read Chapter 13 of the Machine Learning book.
I read Chapters 14 and 15 of the Machine Learning book. The parts will come tomorrow.
I fiinshed the Machine Learning book.
I tested the camera and battery pack that came. They both worked really well. Now, I can start inputting the ML algrothm.
I researched the different datasets I can use for the project, as well as the amount of tree data each have in them. I still need to do some more research, but as of now, creating a dataset is the best option if I want to get a wide, accurate range of data. I haven’t found any datasets that match the amount of data I can collect by myself in my neighborhood.
I researched more about the tree datasets, and I found one that has plenty of accurate information: PlantNet API.
I researched about how to use the specific PlantNet API database with the Raspberry Pi and hook that database to a Machine Learning algorthim. I am finally ready to actually make the Machine Learning algorithm and get my hands dirty now that I have the information I need to continue on with the project.
I connected the database to the Raspberry Pi, and tested it out using a house plant. It worked really well, and the next step is to test it outside with a real tree.
I build a CAD of the structure that will house the prototype. I left holes for all the different parts, and will print it later today.
I 3D printed the top half of the housing for the prototype. Next, I will print the bottom half.
I fixed the issues that the prototype was facing. I also added a button. The main thing I did was make the prototype able to function without the computer nearby, which can make it so that anyone in the world can use it with a battery pack.
I researched the different types of prevalent trees in California and their temperatures during different seasons. I plan to add this to my database, as I hadn’t previously done this. I used many websites, but Our City Forest was the most helpful.
I combined the parts in the bottom half of the prototype, and fit it in the encolsure.
I completed the prototype by combining the enclosure with the prototype. I encased the sensors and the LCD. The prototype is finally complete!
Ships:
I made a project that can detect tree/plant species and temperature to determine whether it is dead or not. It displays the result on an LCD in the back, and will start the process of scanning a tree/plant once a button is pressed. The wiring is enclosed in a 3D printed prototype. The most difficult thing I did in this project was likely trying to fit it all in the enclosure. There were a lot of wires here and there, and the button was also quite difficult to fit in, as I needed to solder it on. However, this project was really fun to do, as it helped me gain knowledge about the environment around me, and I got to practice my software and hardware skills. I am really proud of myself, as I completed this project from start to finish, and didn’t give up. To test my project, all you have to do is plug in the battery pack, wait for a minute or so, point the prototype at the tree you want to scan, press the button, and wait for the results. Sometimes, it will take a little longer to get the results, resulting from a difficulty figuring out either the temperature or species of that particular plant/tree....
|
1792853406.5089006
|
1792939806.5089006
|
|
7574
|
Kolori by @nikooo | Stardance
|
nikooo
|
1
|
142.69
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-06-19T21:34:34Z", "hours": 100.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "Kolori is a desktop application that lets you create beautiful pieces of art from mathematical expressions. Kolori features a custom hand-written recursive descent parser to parse mathematical expressions–which it then uses to transpile your input down to low-level shader instructions to render images to the screen on the GPU. Kolori also supports rendering not only images, but animations using the “ t ” variable in the input expression, which grows linearly with time elapsed–the speed at which is adjustable. Kolori offers four coloring modes that can generate different images based on your input: HSL : Uses the HSL color space to generate pixel colors HSLuv : Uses the HSLuv color space, a perceptually uniform alternative to HSL, to generate pixel colors Custom Palette Generation : Input 4 colors of your choosing so Kolori can generate a custom color palette based on them, to determine pixel colors Use an Image : Select an image using your file manager, or just drag and drop one onto the window for Kolori to sample pixel colors from the input image to determine pixel colors. Making this project was a bit of an adventure, as I had never worked with libraries such as ImGui, SDL, and OpenGL before. The biggest challenge when creating the program was managing memory correctly–which makes me appreciate languages like Nim even more–and code organization.", "repo": "https://github.com/neroist/kolori", "demo": "https://github.com/neroist/kolori/releases/latest"}]...
|
[{"post_id": 18957, "ts": "202 [{"post_id": 18957, "ts": "2026-06-30T04:15:45Z", "hours": 3.4697, "text": "Managed to get everything working <3 The main issue was not assigning texture units to the sampler uniforms whenever the shaders were rebuilt 🤦. Either way, the feature works fully now. The rest that’s left before making a release is chasing some bugs that are left: e.g. crashes on Windows on startup and access violation errors when making a screenshot on fullscreen (also specifically on Windows). Below is the Joukowski function, as I briefly mentioned before in the prior devlog."}, {"post_id": 18224, "ts": "2026-06-29T09:17:52Z", "hours": 16.4414, "text": "After much, much longer than I’d like to admit, I finally got “sliced” coloring to work! Naturally we need a better name, but that can wait. A bit on implementation So, for Kolori, we use OpenGL to render scenes on the GPU. Specifically, we target at minimum desktop OpenGL 3.3, and the GLSL shaders are written for GL ES 3.0 (we plan to target WebGL in the future, maybe moving to WebGPU later. Maybe.). We implement the “slices” coloring mode as follows: The user inputs a list of colors We store these colors as a variable-length array (VLA) which can grow dynamically but, instead of giving these values to the GPU as an array, we instead upload the color data onto the GPU as a one-dimensional texture , since GLSL doesnt support VLAs (and I didn’t want to limit the amount of colors one can add). Though, I did still do that inadvertently. Slice colors are managed by color edit widgets. Each widget’s ID (which must be unique) is generated using the index of the slice color it controls, casted to a byte. Thus, the number of colors cannot exceed 256. Something I hope to fix in the future. Also, an interesting note: GLSL for GLES 3.0 doesn’t support the sampler1D type (which literally represents a 1D texture), so one would have to use the type that represents a 2D texture, but set the height of the texture to 1, to emulate the same behavior and structure. This means that there are now two textures we handle in the shaders: the image texture that holds a user-uploaded image for the “Use an Image” coloring mode, and the slices texture that holds the selected colors for the “(Pizza) Slices” coloring mode. OK, but why’d you take so long? The major obstacle that was ran into was texture data mix-up: the data for the image texture would always overwrite the data for the slices texture, and it would even seem that the two textures were pointing to the same data, even though I assigned separate texture units to each one, and made sure to set the active texture each time before binding textures. OK, can I see it? See the demo below :) They don’t really start to look like “pizza slices” until you have 5+ slices. There are some really interesting graphs you can create using this, such as the graph of the Joukowski function , defined as: f(z)=(z+1/z)/2 (unfortunately not pictured below)."}, {"post_id": 16041, "ts": "2026-06-26T02:00:22Z", "hours": 22.7831, "text": "Progress on 0.4.0 Mainly, two new features/fixes: AppImage builds for Linux (we may add .deb files in releases in the future aswell) Image aspect ratio persistence AppImage builds In past releases, only an executable was published for Linux platforms. This is not a proper solution for distribution–different Linux systems may differ in ABI, available shared libraries, and packages. For instance, on WSL in my Windows machine, SDL3 is not available in the default repository, leaving me unable to build or even run the application. AppImage builds bundle the executable and everything needed for running the application into one file–perfect for distributing. “One app = one file”, or whatever those nerds say. To run on a Linux system, users will only need to do the following: Give the .AppImage file executable permissions Run the app! Its that easy. Image aspect ratio persistence Before, when you put in an image, it would be squished down into a square, distorting the image along its longest dimension. For images with a 1:1 aspect ratio, this is fine. For other images? Not so fine. We now apply proper scaling in the vertex shader pass to textures so that image aspect ratio is preserved in the canvas. Future changes Expect a new coloring mode: “Slices” . This will split the canvas into a number of “slices”–similar to how the four quadrants of the cartesian plane split it up into four slices itself"}, {"post_id": 11476, "ts": "2026-06-18T19:13:22Z", "hours": 5.6697, "text": "I’ve finally added image coloring to Kolori! Kolori can now take an image as input, by either selecting a image file using a native file dialog or dragging and dropping a image onto the window! The image below was created using the feature–using one of the gallery images as the input image. The hard part about this was, not actually loading the image file, reading the pixel data, uploading that into the GPU, and rendering it, but instead managing the file dialog by which a user can upload an image into the application. We use SDL3 for windowing and make use of the SDL_ShowOpenFileDialog function to, well, show an open file dialog. I initially put all of the image loading and OpenGL calls into the dialog callback, but something strange happened then. All of my OpenGL calls were triggering an GL_INVALID_OPERATION error–and specifically on Windows–but why? All of my OpenGL calls were fine, no invalid arguments passed in (or in the wrong order), so everything should’ve been fine. The answer lies in the SDL_ShowOpenFileDialog and how OpenGL works with multiple threads. SDL_ShowOpenFileDialog is an asynchronous function–it will return immediately, and the result of the dialog will be passed on to the callback. However, the callback function may be called on a different thread depending on the operating system, which explains the difference in effect on Windows, and the GL_INVALID_OPERATION errors were caused by the absence of an active OpenGL context on the new thread. SDL3 does not provide an API for setting the current thread’s OpenGL context, so I had to cope. I moved most of the business logic out of the callback function, only keeping the image loading logic, and instead performed the texture uploading into the UI rendering logic, where we check if there is any new image data that happened to be uploaded, upload the data onto the GPU as a texture and show it on screen."}, {"post_id": 11058, "ts": "2026-06-18T02:57:25Z", "hours": 13.5519, "text": "Bumped version from 0.1.0 to 0.2.0! See it here: https://github.com/neroist/kolori/releases/tag/0.2.0 . Some interesting changes are that there is now a VSync toggle + a frame rate slider with support for unlimited frame rate (as much as your GPU can handle), build system improvements–so that Kolori is one make release away on all platforms that have make , and the UI is now DPI-aware and will scale based on the monitor’s DPI (we do not change scaling however if the monitor were to change–something to fix in a future release). We also changed our color input sliders to display RGB colors in their integral components from 0 to 255–what you would expect, rather than a decimal display. See it below!"}, {"post_id": 10217, "ts": "2026-06-16T20:45:34Z", "hours": 5.3461, "text": "Wrote a full-length devlog about a core feature of Kolori and its implementation, but it got deleted for being “too long”. Lets try again then, with a piece that only demands a bit of your time. Kolori offers users three (at the moment) coloring modes: HSL, HSLuv, and custom palette generation. How do these work? Custom palette generation Unlike HSL and HSLuv, using a custom palette to color the complex plane is much more simple. Kolori lets the user input four arbitrary RGB colors. We then use Inigo Quilez’s cosine-based formula for procedural palette generation to determine the color of a pixel. An article of his that describes it is here: https://iquilezles.org/articles/palettes/ . The shader code that does it all: // From https://iquilezles.org/articles/palettes/ vec4 palette ( float t , vec3 a , vec3 b , vec3 c , vec3 d ) { return vec4 ( a + b * cos ( TAU * ( c * t + d )), 1 ); } Our a , b , c , and d are the colors the user has selected, and t is an input variable to vary. However, one would notice that t is a scalar, while we work in the complex numbers. How would we map the plane onto the real line in order to get a valid t to input into palette ? The answer we chose is simple: we take the projection onto the real axis–i.e. just taking the real part of the complex number This is an answer that a user would expect and is pointed out in the user guide. Furthermore, by not choosing ahead of time for the user, we allow the user to exert more control over what they wish to generate."}, {"post_id": 9714, "ts": "2026-06-16T04:10:17Z", "hours": 12.3725, "text": "Was preparing for first our ship, but then discovered that my application was leaking memory–badly. Memory usage would climb by a gigabyte in <2 minutes. Worse than any browser you’ve ever seen. To start, I use Odin, which has a tracking allocator that lets you see where you didn’t free up memory, or perhaps made bad frees. On program exit, it prints out how many bytes you leaked, and where you made the allocations. I had been using this allocator, and it had not reported anything . Furthermore, upon, admittedly, a very cursory look into my code, I couldn’t find anything that would’ve caused any leaks either–all allocations were eventually freed: whether that be manually or via Odin’s temporary allocator (a really nice feature of the language). LSan hadn’t pointed out anything, and Valgrind didn’t report anything scary, only ~200KiB of memory usage. I had even patched myself a custom allocator, which would print to stdout whenever an allocation was made with it, and I saw nothing concerning at all . Only small allocations were made which were, again, eventually freed. My application uses SDL3 for windowing and ImGui for GUI, so perhaps one of those has a memory leak in it? I’m not sure. For such a large library, I doubt that’s the case. Though, when running valgrind with --leak-check=full --show-leak-kinds=all I did keep seeing SDL pop up, but it didn’t report any outright leaks. Upon researching potential external sources for this memory leak, I found https://github.com/libsdl-org/SDL/issues/15192 , which describes my issue exactly. It seems that there is no memory leak in SDL, ImGui, or whatever I’m doing, but instead a driver issue, which is interesting. The example that was given to reproduce the leak is quite minimal, so I wonder how that got past their testing. Furthermore, if this is indeed a driver-level issue, why is it that no other applications running on this device leak memory this egregiously? In any case, the leak is not my fault, and out of my hands."}, {"post_id": 8767, "ts": "2026-06-14T16:04:51Z", "hours": 3.8556, "text": "Learned how cool rubberducking is. I was having a problem fixing a bug with the parser (as described below), and I went to write a devlog about it. As I was describing the bug, I immediately had an idea about how to fix it, and lo and behold, it worked. The original post was going to be as follows: Fixed a bug where trailing input after the parser successfully completes is not disallowed. This most commonly occurs, as I’ve found, after the use of a comma character. The parser simply does not see it, and instead only parses the expression it sees before it. Internally, a comma character is used to delimit function arguments, so this kind of behavior is not totally useless. However, for situations like picrel, it just seems strange. doesn’t it? This usually would be an easy bug to fix. We currently use a recursive descent parser (considering moving to a pratt parser) for parsing mathematical expression. All that honestly would’ve needed to be done was adding a check at the procedure that parses the top level grammar rule: “OK, are we at the end of our input? We should be. If not, return an error.” (Here, I realized that if we literally call this procedure while parsing function calls, it really isn’t too wise to immediately ask “are we done yet?” if we very much aren’t done yet. I then added the check into a procedure that isn’t called by the parser and we should definitely be done by the time that check is ran.)"}, {"post_id": 7892, "ts": "2026-06-13T05:13:05Z", "hours": 13.1858, "text": "(excuse the amount of time, i’m doing general refactors + debugging as well as working on other features) Implemented adjusting the gamma correction amount. Normally, with the gamma correction, areas close to zero would appear black, and areas that have large magnitudes would appear white. However, you might not always want this, and instead would prefer only pure hue . Set the gamma correction amount to zero to reduce the amount that colors’ lightness values are altered. An example is shown where we use this feature to produce a colorful, moving spiral. Note that when gamma correction is not zero, as demonstrated, the effect does not work as well."}, {"post_id": 7005, "ts": "2026-06-11T19:13:46Z", "hours": 32.8386, "text": "The project has come a long way (see the 30 hours of work that went into this). We now support: arbitrary function input on-the-fly graphing and error checking a “t” time variable, allowing you to animate graphs being able to speed up, slow down, and pause animations screenshots (not imaged below) zoom in/out panning As well as being multiplatform for both Linux and Windows (you can go build and run this, right now ). What to expect: the ability to change coloring method (hsl, hsluv, palette generation) the ability to use an image for coloring WASM web builds coordinate inspector to see where exactly in the complex plane you are Check it out on github: https://github.com/neroist/kolori"}, {"post_id": 3571, "ts": "2026-06-06T15:51:37Z", "hours": 12.8247, "text": "Finally! SDL3 & the GPU shaders are working. FWIW – this is the graph of f(z)=z, the identity transformation. I will make a devlog on how these images are generated later."}]...
|
Title: Kolori by @nikooo | Stardance
Devlogs:
Fin Title: Kolori by @nikooo | Stardance
Devlogs:
Finally! SDL3 & the GPU shaders are working. FWIW – this is the graph of f(z)=z, the identity transformation. I will make a devlog on how these images are generated later.
The project has come a long way (see the 30 hours of work that went into this). We now support: arbitrary function input on-the-fly graphing and error checking a “t” time variable, allowing you to animate graphs being able to speed up, slow down, and pause animations screenshots (not imaged below) zoom in/out panning As well as being multiplatform for both Linux and Windows (you can go build and run this, right now ). What to expect: the ability to change coloring method (hsl, hsluv, palette generation) the ability to use an image for coloring WASM web builds coordinate inspector to see where exactly in the complex plane you are Check it out on github: https://github.com/neroist/kolori
(excuse the amount of time, i’m doing general refactors + debugging as well as working on other features) Implemented adjusting the gamma correction amount. Normally, with the gamma correction, areas close to zero would appear black, and areas that have large magnitudes would appear white. However, you might not always want this, and instead would prefer only pure hue . Set the gamma correction amount to zero to reduce the amount that colors’ lightness values are altered. An example is shown where we use this feature to produce a colorful, moving spiral. Note that when gamma correction is not zero, as demonstrated, the effect does not work as well.
Learned how cool rubberducking is. I was having a problem fixing a bug with the parser (as described below), and I went to write a devlog about it. As I was describing the bug, I immediately had an idea about how to fix it, and lo and behold, it worked. The original post was going to be as follows: Fixed a bug where trailing input after the parser successfully completes is not disallowed. This most commonly occurs, as I’ve found, after the use of a comma character. The parser simply does not see it, and instead only parses the expression it sees before it. Internally, a comma character is used to delimit function arguments, so this kind of behavior is not totally useless. However, for situations like picrel, it just seems strange. doesn’t it? This usually would be an easy bug to fix. We currently use a recursive descent parser (considering moving to a pratt parser) for parsing mathematical expression. All that honestly would’ve needed to be done was adding a check at the procedure that parses the top level grammar rule: “OK, are we at the end of our input? We should be. If not, return an error.” (Here, I realized that if we literally call this procedure while parsing function calls, it really isn’t too wise to immediately ask “are we done yet?” if we very much aren’t done yet. I then added the check into a procedure that isn’t called by the parser and we should definitely be done by the time that check is ran.)
Was preparing for first our ship, but then discovered that my application was leaking memory–badly. Memory usage would climb by a gigabyte in <2 minutes. Worse than any browser you’ve ever seen. To start, I use Odin, which has a tracking allocator that lets you see where you didn’t free up memory, or perhaps made bad frees. On program exit, it prints out how many bytes you leaked, and where you made the allocations. I had been using this allocator, and it had not reported anything . Furthermore, upon, admittedly, a very cursory look into my code, I couldn’t find anything that would’ve caused any leaks either–all allocations were eventually freed: whether that be manually or via Odin’s temporary allocator (a really nice feature of the language). LSan hadn’t pointed out anything, and Valgrind didn’t report anything scary, only ~200KiB of memory usage. I had even patched myself a custom allocator, which would print to stdout whenever an allocation was made with it, and I saw nothing concerning at all . Only small allocations were made which were, again, eventually freed. My application uses SDL3 for windowing and ImGui for GUI, so perhaps one of those has a memory leak in it? I’m not sure. For such a large library, I doubt that’s the case. Though, when running valgrind with --leak-check=full --show-leak-kinds=all I did keep seeing SDL pop up, but it didn’t report any outright leaks. Upon researching potential external sources for this memory leak, I found https://github.com/libsdl-org/SDL/issues/15192 , which describes my issue exactly. It seems that there is no memory leak in SDL, ImGui, or whatever I’m doing, but instead a driver issue, which is interesting. The example that was given to reproduce the leak is quite minimal, so I wonder how that got past their testing. Furthermore, if this is indeed a driver-level issue, why is it that no other applications running on this device leak memory this egregiously? In any case, the leak is not my fault, and out of my hands.
Wrote a full-length devlog about a core feature of Kolori and its implementation, but it got deleted for being “too long”. Lets try again then, with a piece that only demands a bit of your time. Kolori offers users three (at the moment) coloring modes: HSL, HSLuv, and custom palette generation. How do these work? Custom palette generation Unlike HSL and HSLuv, using a custom palette to color the complex plane is much more simple. Kolori lets the user input four arbitrary RGB colors. We then use Inigo Quilez’s cosine-based formula for procedural palette generation to determine the color of a pixel. An article of his that describes it is here: https://iquilezles.org/articles/palettes/ . The shader code that does it all: // From https://iquilezles.org/articles/palettes/ vec4 palette ( float t , vec3 a , vec3 b , vec3 c , vec3 d ) { return vec4 ( a + b * cos ( TAU * ( c * t + d )), 1 ); } Our a , b , c , and d are the colors the user has selected, and t is an input variable to vary. However, one would notice that t is a scalar, while we work in the complex numbers. How would we map the plane onto the real line in order to get a valid t to input into palette ? The answer we chose is simple: we take the projection onto the real axis–i.e. just taking the real part of the complex number This is an answer that a user would expect and is pointed out in the user guide. Furthermore, by not choosing ahead of time for the user, we allow the user to exert more control over what they wish to generate.
Bumped version from 0.1.0 to 0.2.0! See it here: https://github.com/neroist/kolori/releases/tag/0.2.0 . Some interesting changes are that there is now a VSync toggle + a frame rate slider with support for unlimited frame rate (as much as your GPU can handle), build system improvements–so that Kolori is one make release away on all platforms that have make , and the UI is now DPI-aware and will scale based on the monitor’s DPI (we do not change scaling however if the monitor were to change–something to fix in a future release). We also changed our color input sliders to display RGB colors in their integral components from 0 to 255–what you would expect, rather than a decimal display. See it below!
I’ve finally added image coloring to Kolori! Kolori can now take an image as input, by either selecting a image file using a native file dialog or dragging and dropping a image onto the window! The image below was created using the feature–using one of the gallery images as the input image. The hard part about this was, not actually loading the image file, reading the pixel data, uploading that into the GPU, and rendering it, but instead managing the file dialog by which a user can upload an image into the application. We use SDL3 for windowing and make use of the SDL_ShowOpenFileDialog function to, well, show an open file dialog. I initially put all of the image loading and OpenGL calls into the dialog callback, but something strange happened then. All of my OpenGL calls were triggering an GL_INVALID_OPERATION error–and specifically on Windows–but why? All of my OpenGL calls were fine, no invalid arguments passed in (or in the wrong order), so everything should’ve been fine. The answer lies in the SDL_ShowOpenFileDialog and how OpenGL works with multiple threads. SDL_ShowOpenFileDialog is an asynchronous function–it will return immediately, and the result of the dialog will be passed on to the callback. However, the callback function may be called on a different thread depending on the operating system, which explains the difference in effect on Windows, and the GL_INVALID_OPERATION errors were caused by the absence of an active OpenGL context on the new thread. SDL3 does not provide an API for setting the current thread’s OpenGL context, so I had to cope. I moved most of the business logic out of the callback function, only keeping the image loading logic, and instead performed the texture uploading into the UI rendering logic, where we check if there is any new image data that happened to be uploaded, upload the data onto the GPU as a texture and show it on screen.
Progress on 0.4.0 Mainly, two new features/fixes: AppImage builds for Linux (we may add .deb files in releases in the future aswell) Image aspect ratio persistence AppImage builds In past releases, only an executable was published for Linux platforms. This is not a proper solution for distribution–different Linux systems may differ in ABI, available shared libraries, and packages. For instance, on WSL in my Windows machine, SDL3 is not available in the default repository, leaving me unable to build or even run the application. AppImage builds bundle the executable and everything needed for running the application into one file–perfect for distributing. “One app = one file”, or whatever those nerds say. To run on a Linux system, users will only need to do the following: Give the .AppImage file executable permissions Run the app! Its that easy. Image aspect ratio persistence Before, when you put in an image, it would be squished down into a square, distorting the image along its longest dimension. For images with a 1:1 aspect ratio, this is fine. For other images? Not so fine. We now apply proper scaling in the vertex shader pass to textures so that image aspect ratio is preserved in the canvas. Future changes Expect a new coloring mode: “Slices” . This will split the canvas into a number of “slices”–similar to how the four quadrants of the cartesian plane split it up into four slices itself
After much, much longer than I’d like to admit, I finally got “sliced” coloring to work! Naturally we need a better name, but that can wait. A bit on implementation So, for Kolori, we use OpenGL to render scenes on the GPU. Specifically, we target at minimum desktop OpenGL 3.3, and the GLSL shaders are written for GL ES 3.0 (we plan to target WebGL in the future, maybe moving to WebGPU later. Maybe.). We implement the “slices” coloring mode as follows: The user inputs a list of colors We store these colors as a variable-length array (VLA) which can grow dynamically but, instead of giving these values to the GPU as an array, we instead upload the color data onto the GPU as a one-dimensional texture , since GLSL doesnt support VLAs (and I didn’t want to limit the amount of colors one can add). Though, I did still do that inadvertently. Slice colors are managed by color edit widgets. Each widget’s ID (which must be unique) is generated using the index of the slice color it controls, casted to a byte. Thus, the number of colors cannot exceed 256. Something I hope to fix in the future. Also, an interesting note: GLSL for GLES 3.0 doesn’t support the sampler1D type (which literally represents a 1D texture), so one would have to use the type that represents a 2D texture, but set the height of the texture to 1, to emulate the same behavior and structure. This means that there are now two textures we handle in the shaders: the image texture that holds a user-uploaded image for the “Use an Image” coloring mode, and the slices texture that holds the selected colors for the “(Pizza) Slices” coloring mode. OK, but why’d you take so long? The major obstacle that was ran into was texture data mix-up: the data for the image texture would always overwrite the data for the slices texture, and it would even seem that the two textures were pointing to the same data, even though I assigned separate texture units to each one, and made sure to set the active texture each time before binding textures. OK, can I see it? See the demo below :) They don’t really start to look like “pizza slices” until you have 5+ slices. There are some really interesting graphs you can create using this, such as the graph of the Joukowski function , defined as: f(z)=(z+1/z)/2 (unfortunately not pictured below).
Managed to get everything working <3 The main issue was not assigning texture units to the sampler uniforms whenever the shaders were rebuilt 🤦. Either way, the feature works fully now. The rest that’s left before making a release is chasing some bugs that are left: e.g. crashes on Windows on startup and access violation errors when making a screenshot on fullscreen (also specifically on Windows). Below is the Joukowski function, as I briefly mentioned before in the prior devlog.
Ships:
Kolori is a desktop application that lets you create beautiful pieces of art from mathematical expressions. Kolori features a custom hand-written recursive descent parser to parse mathematical expressions–which it then uses to transpile your input down to low-level shader instructions to render images to the screen on the GPU. Kolori also supports rendering not only images, but animations using the “ t ” variable in the input expression, which grows linearly with time elapsed–the speed at which is adjustable. Kolori offers four coloring modes that can generate different images based on your input: HSL : Uses the HSL color space to generate pixel colors HSLuv : Uses the HSLuv color space, a perceptually uniform alternative to HSL, to generate pixel colors Custom Palette Generation : Input 4 colors of your choosing so Kolori can generate a custom color palette based on them, to determine pixel colors Use an Image : Select an image using your file manager, or just drag and drop one onto the window for Kolori to sample pixel colors from the input image to determine pixel colors. Making this project was a bit of an adventure, as I had never worked with libraries such as ImGui, SDL, and OpenGL before. The biggest challenge when creating the program was managing memory correctly–which makes me appreciate languages like Nim even more–and code organization....
|
1792835260.6475484
|
1792921660.6475484
|
|
36348
|
Chess Bot by @GunRattler28 | Stardance
|
GunRattler28
|
1
|
143.67
|
2096
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-21T06:00:32Z", "hours": 127.0, "multiplier": 16.58, "payout": 2096, "blessing": "neutral", "text": "I’m finally shipping the chess bot! So excited to see what people think of it :) . Before you try my project I highly suggest reading the README (on my github page https://github.com/GunRattler28/Chess-Bot ). It has a full list of all controls which will help you understand how to play. Anways, can’t wait to see what you guys think of it!", "repo": "https://github.com/GunRattler28/Chess-Bot", "demo": "https://github.com/GunRattler28/Chess-Bot/releases"}]...
|
[{"post_id": 80354, "ts": "202 [{"post_id": 80354, "ts": "2026-10-17T11:49:41Z", "hours": 0.8044, "text": "So, I’ve actually decided to work on making my bot playable on a website since that was one of the main things people said they would have wanted last ship and stardance is ending soon. But thats not why I’m posting. I’m posting because I spent the last 40 minutes rooting out a bug that caused my premoves to not be played. My first step was playing the release version that can be downloaded to see if the bug was there as well. Luckily it wasn’t so I went to my current commit on my main branch and kepy undoing commits (and saving them with git stash ) until the bug was gone. I then found that the bug only became a thing after I merged my commenting branch. Because of this I did the same thing of undoing commits and saving them on the commenting branch til I found the commit that caused the bug. I then went through all the changes until I found the problem. Then I fixed the bug, merged the fix to main then merged main into my other branches so that they would have the bug fix as well. I’ve already done some research and have found that pygbag seems to fit my needs for turning my pygame program into a website pretty well. Due to this I will probably use pygbag to get a website version of my chess bot up and running! Attached is a screenshot of the commit that caused the bug. Before the commit is on the left and after the commit is on the right. Try to find what caused the bug! If you can’t check the comments of the post as I am gonna say what the problem was there. I also attached a photo of how my git tree looks now and… it looks sooo messy lol."}, {"post_id": 79553, "ts": "2026-10-14T16:01:16Z", "hours": 2.835, "text": "I started getting bored of just commenting my code so I created a new branch in which I am trying to make my bot be able to play other bots. After some research, I found that the engine needs to be able to take in moves in the form of algebraic notation (a type of shorthand notation for moves. for example: Be4). Because of this I read a Wikipedia page on algebraic notation and made a function to convert between it and the parameters my makeMove() takes, that being: startRow , startColumn , endRow and endColumn . I have done this by first checking if the move is a castle (notated by O-O or O-O-O) then forming the full start and end coordinates by hardcoding the column values and finding the row based off the colour that is castling. Then I find what piece is moving (shown by the first letter) and if there is a promotion (shown by ‘=’ sign). Algebraic also has characters to show captures (x), checkmates (#) and checks (+), however the engine does not need these and so they are removed in 3 separate while loops. At this point I am left with just the end coordinates and maybe a combination of start coordinates. To find the full start coordinates (as they are all I am missing) I loop through the specific bitboard for the piece and whenever I find a square where there is a piece, I check whether it matches with the start coordinates I have, and if it does whether it can legally reach the end square. If it can then that is my piece and I return its start coordinates and the end coordinates it moves to (which is given in the input string). Currently, this function lives inside a random file in my project folder but my next step will be to properly integrate it into the rest of the code."}, {"post_id": 78191, "ts": "2026-10-09T22:40:08Z", "hours": 7.2036, "text": "Yup! I did what I said I would last time, and finished commenting ALL of logic.py . I don’t think I can describe how long this took and how annoying it was to have to go through 750+ lines of code and add comments explaining things. And then I realise that there’s just random stuff that I don’t even need. Anyway, I’m pretty satisfied that I managed to comment so much. This is definitely the largest file and so I’m happy I’ve gotten it out of the way. There’s not really much I can’t say like explaining and stuff because all I’ve done for the last 5 hours is explain stuff. Anyway once I’ve commented everything, I’ll probably make another devlog and then go and work on something else."}, {"post_id": 72816, "ts": "2026-09-23T19:46:37Z", "hours": 5.8231, "text": "Thanks for all the positive feedback and constructive criticism on my ship! Working to improve where people said and I am starting by trying to add comments to my code to explain what is happening. So far I’ve fully commented 3 / 7 files and am around 30% through commenting logic.py which contains most of the complicated code. Its taking a while but I really hope that this will help people understand what I am doing and that they will like my project more because of this. Next I am going to continue adding comments to logic.py and will probably have to make a devlog after just that since the file is pretty big and has slightly complicated stuff in it. Once again thanks to all the people who rated the project or gave feedback!"}, {"post_id": 71494, "ts": "2026-09-20T22:33:25Z", "hours": 2.5339, "text": "I’ve made a few small changes that help slightly speed the bot up. I stopped checking whether I have surpassed the time limit every move I search and instead every 2048 moves I search. This is better as it removes thousands of unnecessary library calls which each add a small amount of time to how long each move takes to search. Since computers are very fast 2048 doesn’t actually take long at all but it massively reduces the amount of times the pygame library is called. I also changed my move ordering to sort manually instead of with in a lambda function since this lambda functions are more inefficient. I am next going to ship the project!!!"}, {"post_id": 69354, "ts": "2026-09-16T21:16:32Z", "hours": 3.3497, "text": "Premoves finished! My premoves are now fully implemented and the branch is merged. Making the move with middle click removes that specific premove. Middle clicking a random square clears all premoves and arrows. I also added principal variation search (pvs) which is just searching moves that aren’t the previous best move with a tight move score boundary then researching them normally if they are better than expected. I am probably going to ship soon and so will start polishing the project up now."}, {"post_id": 63850, "ts": "2026-09-07T20:35:48Z", "hours": 6.9336, "text": "Way better premoves. I’ve updated my premove system to store multiple premoves. I did this by storing eaach premove as a tuple of ( startRow , startColumn , endRow , endColumn ). These tuples contain all the data I need to know and can be easily appended into an array. To clear a premove you can either do the same move again which removes that specific premove or tap the middle mouse button anywhere which clears all premoves and arrows. I also render a translucent version of the piece that I’ve moved to its premove destination and remove the texture for where it was originally. This helps you keep track of what your premoves are actually doing. I do this by drawing the board normally then looping through each move in the premoves array and drawing that piece at its correct destination in reduced alpha. Next I am going to try to improve my bot further so that I can push to a depth of around 10 during midgame."}, {"post_id": 59871, "ts": "2026-09-01T21:01:21Z", "hours": 5.3678, "text": "I’ve added FEN strings! FEN strings (Forsyth-Edwards Notation) are a way to store positions of chess games. They are used by many chess engines to allow players to start from different positions or midway through games. Adding these wasn’t too difficult as the strings are formatted in a nice way. I simply split up the FEN string into its 6 parts and set my variables to the value in the FEN string. To test if it worked I inputted the FEN string 4kb1r/p2n1ppp/4q3/4p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 1 16 which gives the position of the famous opera game directly before the queen sacrifice. I then set the bot colour to white and was glad to see that it immediately found the best move (the queen sacrifice). I also added premoves so that I can choose my move in advance. I did this by just storing the move I make when it is the bot’s turn and playing it as soon as the bot plays. However I can only premove 1 move ahead. Next I will try to allow the user to premove multiple moves ahead."}, {"post_id": 52243, "ts": "2026-08-22T10:35:52Z", "hours": 2.8078, "text": "Quiescent search is amazing! After adding it my bot instantly became better. A quiescent search just means that when evaluating a board state to a certain depth even if you reach that depth you need to evaluate until a quiet move (a move that isn’t a capture). This means that even if the bot reaches the max depth in the middle of a piece trade it won’t just think the move is amazing since all it can see it won a piece. Since I added the bot, when tested against other bots it has made 0 blunders! In fact it beat Li, a bot rated 2000 on chess.com ! I’m really proud of this and think it is really cool to say that my bot has beaten a 2000 rated bot. However, it only beat the bot once and so it could just be lucky. I was going to test it in 2 more games against the same bot but found myself getting bored of manually inputting moves into chess.com and my bot to get them to play each other. Therefore, I’m planning on making my bot able to play other bots on its own. To do this I will need to download the other bots and make my bot give properly formatted inputs and outputs."}, {"post_id": 51300, "ts": "2026-08-20T21:17:05Z", "hours": 3.5539, "text": "Massive improvements in depth! I added 2 changes: null move pruning and late move reduction. Null move pruning is when the bot ‘passes its turn’, when searching for the best move. The bot then evaluates the board at a reduced depth after letting the opponent play twice in a row. If it is still in a winning position then that means it is winning by a lot. This means that it can prune this branch since the opponent wouldn’t let the game get to this state and would make a better decision earlier in the tree. Since the opponent wouldn’t let the game get to this it means that further analysis isn’t necessary. Late move reduction works on the basis that I have good move ordering, which I just worked on so I would hope I do. Late move reduction means that I search the later moves at a reduced depth since the best moves are at the start and so later moves are likely bad. If they turn out to be good after the reduced evaluation depth then they are researched at the full depth. This helps reduce the amount of moves which need to be evaluated by a lot. Together null move pruning and late move reduction have taken my depth from around 6 - 7 in midgame to around depth 7 - 10 in midgame! In endgame this is even more. When I tested it in endgame it got to a depth of 13! My next change is something called quiescent search which will boost the bot’s elo by massive amounts as it should mostly stop the bot from making blunders."}, {"post_id": 50191, "ts": "2026-08-19T10:05:52Z", "hours": 1.9161, "text": "Move ordering improved! I’ve merged my SCRUM-29/move-ordering branch with main and now have my improved move ordering in main. This has let me go from a depth of 5 to almost always a depth of at least 6. I then merged main into SCRUM-42/improve-maintainability so that I could add comments to the code I added from the move ordering branch. Next I am going to add some comments explaining what things do."}, {"post_id": 48364, "ts": "2026-08-16T17:56:17Z", "hours": 4.4589, "text": "I’ve massively improved my organisation! I’m now properly utilising branches which is allowing me to work on multiple changes at the same time. Currently I am working on 2 branches. One is about improving my maintainability by adding comments so that other people can understand what I am doing and why. This also has the added benefit of me being able to make some small improvements and bug fixes as I am reading through all of my code. The second branch is about move ordering which is super important as it makes my alpha beta pruning more effective. Working on these changes on branches is useful because it not only keeps it organised and easy to see what each commit is for but also means that if I decide I don’t want the change anymore it is easy to remove as it isn’t mixed in with all the other changes. To help with this I’ve also created a Jira board which helps me keep my ideas organised. My branches are called SCRUM-NUMBER/NAME . The scrum number is given to me by Jira. As well as improving my organisation I also further improved my zobrist hashing by replacing using modulo on the hash to get the index of the transposition with using an and gate between my hash and the table size - 1 (which is just all 1s and a single 0 on the left most digit since the table size is a power of 2). I also stored my castle rights as a single 4 digit binary digit where each digit represents whether a certain rook has castling rights or not. My logic with this was since the point of bitboards is 12 integer is better than 64 integers, a single 4 digit integer is better than a dictionary of 4 values but I am not actually sure if this is true or not. On the maintainability branch so far I have added comments to all of main.py . On the move ordering branch I have improved my sorting from just sorting by most valuable capture and least valuable attacker which is only good for captures to also scoring promotions very highly. I also instantly push the previous best move to the front of the queue and score moves which cutoff branches in alpha beta pruning highly as well. Next I will try to add comments to one of my other files and improve my move ordering by not using lambda functions."}, {"post_id": 47220, "ts": "2026-08-15T00:10:15Z", "hours": 4.5128, "text": "I’ve improved my endgame condition. When testing the bot I found that during endgame it would sometimes move its king to the corners of the board. I realised that this was because it saw a queen on the board and due to the poor endgame condition it thought this meant that it wasn’t endgame and instead used the early / mid game position bonus table which rewards staying safe in the corners. However in endgame moving the king to the corner isn’t good as it makes checkmates easier for the opponent and means that the king isn’t used to help checkmate the opponent, which is crucial in certain endgames such as a king and queen against just a king. In situations like this the king is necessary in achieving a checkmate. I changed it to being if there is less than (2 black queens and 2 white queens and less than 16 pieces) or if there is less than 5 pieces not including pawns. This is more accurate and ensures the king actually helps win and tries to stop a checkmate on itself. I also improved my zobrist hashing by fully incrementing it. Previously I incremented it for everything except castling and en passant however now these are implemented as well. I also noticed that the bot would walk its king towards the centre of the board during the opening which puzzled me until I read through my code and realised its because I set totalPieces to 0, asked if it was endgame, and afterwards set totalPieces to the true value. This was pretty difficult to find as I couldn’t see it by just seeing if it thought it was endgame at the end of a move. This makes sure I can actually get my hashes in O(1) time. Next I am going to try and make my arrows darker when over each other."}, {"post_id": 46626, "ts": "2026-08-14T00:35:40Z", "hours": 3.7133, "text": "I’ve added a better endgame condition! Now endgame is determined if (there are no queens and less than 16 pieces on the board) or if there is less than 8 pieces on the board. This is better because it means when pawns promote the king doesn’t run to the corners or use the other position bonus table. I also improved my bot’s 3 fold repetition check when searching for moves by using a dictionary with hashes as keys and number of times they have appeared as values. This lets me check if I have been in the position 3 times before in O(1) instead of O(n) . I also improved my makeMove() and previousMove() functions by checking if its the bot searching for moves and if it is then only storing the key information instead of everything. To help simplify previousMove() I instead created a seperate function called unmakeMove() (clearly related to makeMove() ) to undo moves when the bot is searching. I also changed the time the bot has to move from 3 seconds to 1.5 seconds so that it feels snappier. Probably cos I have 0 patience when I want to test if the bot works :). Next I am going to try and make my bot faster by improving the move ordering so that alpha beta pruning can be more effective."}, {"post_id": 46469, "ts": "2026-08-13T18:10:03Z", "hours": 6.67, "text": "Iterative deepening! The bot now searches depths in an ascending order instead of descending. This is only possible thanks to my transposition tables but means that I can give the bot a time limit. Once the time limit is reached it just return the best move of the highest depth its fully searched. This means that I can search to different depths in different positions whilst making sure I always play within the time limit. This is good as it means I can have a high depth during the opening and endgame whilst having a medium depth during mid game instead of having a medium depth everywhere to ensure moves at midgame don’t take too long. Midgame usually has a lower depth due to the high number or possible moves that can be played. I can also easily change the time limit with just a single variable. This means I can get my bot to play faster but also worse or take longer but play better. I added incremental evaluation. This means that for each move, instead of looping through all 12 bitboards and recalculating the evaluation score from scratch I instead update it from the current evaluation score. So when I move a piece I subtract its old it’s material value and then add the material value if it still exists. The same happens for the position bonuses. This takes my time complexity from O(n) to O(1) which is a massive upgrade. These changes lets my depth in early game and endgame almost always stay at 7 whilst during midgame hover between depth 6 and depth 7. Currently I determine if I am in endgame if both queens are gone and the total amount of pieces is below 16 however I feel that this isn’t a good measure and I should improve this. Next I will try to improve efficiency by improving my move ordering so that the alpha beta pruning is more efficient."}, {"post_id": 45212, "ts": "2026-08-11T15:18:18Z", "hours": 7.8964, "text": "Transposition tables! I’ve added zobrist hashing so that I can create a dictionary that contains all of the board states I have already evaluated. Since many different moves can lead to the same position it means that I can save computing power by just getting the score of that board state instead of having to re-evaluate the board again. Zobrist hashes are just a way to represent the board in hash. This is useful as I can use them as the keys in my dictionary and quickly see if the current board state has been evaluated yet. I also fixed a bug that let the bot play illegal moves as there wasn’t a legality check after the aspiration window failed. An abort flag was also added that is set to True when the user undos or redos a move. This lets me instantly stop that thread’s search for the best move ensuring it doesn’t use unneccessary computing power. Next I am going to try and implement iterative deepening so that I can force the bot to move within a certain amount of time. This would let me make the bot play different ‘time controls’ by altering how long it has to think about what move it should play. I tested it against some bots from chess.com and it managed to beat Wally (a 1800 bot) but lost to Li (a 2000 bot)"}, {"post_id": 43276, "ts": "2026-08-08T13:19:50Z", "hours": 9.4272, "text": "Improved efficiency! I’ve improved the efficiency of the bot by sorting the possible moves better. This is useful as it means when the alpha beta pruning goes over everything it searches the best trees first. This means that it can prune more branches. I also removed some random integer conversions on my bitboards as they did nothing, since the bitboards are already integers. I also removed a restriction that didn’t let the player do anything on the bot’s turn. Now the player can draw during the bot’s turn. I also went across all of my code and changed it to use (row, column) instead of (column, row). This helps keep things cleaner. I then also changed the bot calculations from from using the fullyLegalMoves() function to the movementLegalMoves function(). fullyLegalMoves() just makes sure that the king isnt in check after the move. If it is then it undoes the move. By insteading doing this in my bot.py it means I can just not undo the move and immediately remove that move. This means that I don’t need to undo the move more times than needed. This is very helful as undoing a move takes up computing power. I also completely replaced the piece names with integers as this would be slightly better and I could use bit operations with integers. As well as this I also added a timer that shows how long the bot took to make its move. By adding all of these changes the bot reduced its time on the first move as white from ~4 seconds (with depth 4) to 3 seconds (with depth 5). This shows that it is much more efficient as, time taken increases exponentially as depth increases. As well as this 3 seconds is the earliest the bot can move and so the true speed is likely much less."}, {"post_id": 40993, "ts": "2026-08-04T21:24:23Z", "hours": 1.4964, "text": "Better arrows! I don’t remember where but there is one place where drawing arrows in a knights movement creates an L shaped arrow instead of just a normal arrow. I love this and so implemented it into my program. I also added aspiration windows. Aspiration windows work on the basis that moves don’t usually swing the score by a lot. This means that when going down paths we can set the lower and upper bounds to previous score ± window size. This helps prune more paths. If we find that the score is actually higher than we expect we search again but without the aspiration window. This helps remove lots of the obviously bad paths which makes the program much more efficient. Next I am going to add zobrist hashing so that I can use transposition tables."}, {"post_id": 40821, "ts": "2026-08-04T16:15:52Z", "hours": 2.5794, "text": "Colour randomisation! The colour you play as is chosen at random at the start of the game. The board is also automatically rotated so that you are at the bottom and the bot is at the top. This also helps to show which colour you are playing as. I also show the last move by making the starting square #97C997 and the ending square #8DCE8D. I did this as when I asked I watched someone play it I noticed that they would often undo the move, to see what the bot just did, then redo the move again. This helps make everything feel more polished. I like the ending square colour ( #8DCE8D ) but I’m not sure if I like the starting square colour ( #97C997 ). These colours are very easy to change though and so I can quickly edit them to test out different colours. I also realised that when I eventually ship the project most people who download it won’t have the font I use ( dynapuff ) installed. I tested it out to see what it is like without it and found that it crashed. To fix this, I implemented a try - except statement which sees if dynapuff exists on the user’s computer. If it does then it uses dynapuff which is how it is supposed to be played but if they don’t have the font it uses arial instead. Due to the strange spacing of dynapuff I used double spaces between the words on the game over messages, but when using arial the double spaces look quite strange. I also tweaked some of the position bonus values to further encourage castling. Next I am going to try to add a file to reward moves which keep keep the king safe."}, {"post_id": 40296, "ts": "2026-08-03T18:45:17Z", "hours": 10.2542, "text": "I’ve added position bonuses! I created a new file which rewards moves which place certain pieces in certain areas. For example pawns are rewarded for controlling the centre and pushing to the 7th rank (so that they can promote). The bot now tries to control squares and this means it now has proper openings. I also completely updated the file structure to split the responsibilities more clearly. I also updated main.py and logic.py and made them classes. Making logic.py a class means that I can use it for both the player actions and bot actions without them interfering. This also makes my previousMove() much cleaner as it means when the bot uses it, it doesn’t interfere with when the player uses it. Due to the new file structure it means that I can easily change the weights of each of the different modules to change how the bot plays easily. Later I will add a slider or at least a setting that lets the player control these modules. As well as this I also changed it so that drawing the same arrow again removes the arrow. Next I am going to try add another module that rewards moves which keep the king safe."}, {"post_id": 37668, "ts": "2026-07-30T15:47:33Z", "hours": 7.4286, "text": "I’ve added a bot! The main point of this was to link the new file I made ( bot.py ) to main.py . The bot currently just chooses moves based off of what would result in the best material difference between white (player) and black (bot). It calculates these moves with a depth of 4. Since the main point wasn’t to actually make a bot in one go the bot isn’t very good currently. However I still added alpha beta pruning. Alpha beta pruning works by storing the best guaranteed outcome for you (alpha) and the best guaranteed outcome for the opponent (beta). Whenever it finds a path where the opponent can force a move that is worse than the alpha, it immediately stops going down that path. The same happens for beta. I also removed some duplicated code in main.py and fixed the undo and redo move logic as I forgot to check if it still worked properly after switching from tkinter to pygame. Currently you can only tap the buttons and but can’t hold to go back multiple moves. Undoing and redoing moves with a bot was a bit confusing for me and so I decided to make it so that when undoing and redoing there is 3 seconds before the bot moves. During these 3 seconds you can go forwards or backwards to reset the timer. I used threading so that the bot starts to calculate what move it would make at the same time as the 3 seconds are passing. This means that if you decide to stay on the turn you don’t need to wait as long. I also blurred the background of the the promotion GUI so that it is grabs the players attention. Next I am going to try and add a better bot by assigning each possible move a score that is based off of the squares it controls, offensive and defensive opportunities and potentials for forks (when 2 pieces are being attacked by 1 piece so only one piece can move out of danger)."}, {"post_id": 36896, "ts": "2026-07-29T08:41:59Z", "hours": 1.9431, "text": "I’ve split my file into 5 now. There is moveGeneration.py which calculates all the legal moves, moveExecution.py which actually moves the pieces, updateBoard.py which updates the board, gui.py which handles graphics (like arrows, promotion page and clicks) and main.py which says what functions should happen when an input is given and has a while loop for while the game is running (as is needed by pygame). Splitting the file makes it much easier to debug, keep track and add features instead of just having one ~1000 line file where everything is jumbled up. Next I am going to start work on the actual bot."}, {"post_id": 36538, "ts": "2026-07-28T18:38:30Z", "hours": 1.1517, "text": "Insufficient material and 50 move rule! These were the last things I needed to add before step 1 was complete. Now the next step is to actually add the bot! I’ve got everything I need down for a game of chess before 2 players. I added insufficient material by creating a new insufficientMaterial() . Inside the function are some if statements that determine whether winning is still possible (not assisted by opponent going into the corner). I also added the 50 move rule by checking moves since last pawn advancement or capture and if it exceeds 50 then end the game. I also made it so that once the game ends you can go undo moves and continue playing instead of just having the game end. Next I am going to try to clean up my code, split the code into multiple files and optimise it."}, {"post_id": 36025, "ts": "2026-07-27T20:54:24Z", "hours": 1.3014, "text": "En passant has been added. I find if it is legal by making sure the pawn capturing is on its fifth rank and the pawn being captured is moved 2 forward. Thats all I need for en passant (also only available for the first go it is there). I also made my program more efficient by ensuring I only rely on an array for finding what piece is on an index and drawing. Everything else uses bitboards which helps me fully utilise bitboards. I now have 2 things left: 50 move rule and Insufficient Material. Both of these end in a draw and are the last things I need for my program to have all the rules of chess! Next I will add the 50 move rule"}, {"post_id": 34600, "ts": "2026-07-25T13:43:51Z", "hours": 2.65, "text": "I was so unimpressed by how my old arrows looked I just completely switched libraries! I now use pygame instead of tkinter. It offers much more graphical customisability and now I can draw arrows (and other shapes) with true transparency instead of just stipples. One of the main downsides of pygame is that it constantly redraws each frame. This takes computing power and drains battery. To stop this I created a redraw boolean which tells my script when to redraw. This means instead of redrawing every single frame I redraw whenever something actually changes. Due to pygame I also customised the arrows so that the tails are rounded. This means that multiple arrows from the same point looks much cleaner. I also get true translucency instead of stipples and I added an icon!"}, {"post_id": 34132, "ts": "2026-07-24T17:06:08Z", "hours": 4.1839, "text": "Need to get your strategies into something you can see? Lines let you do just that. Right clicking and dragging draws a line that follows your cursor around. Right clicking again places the line. The lines are automatically centred so that they snap to the centre of the squares. If you line starts and ends at the same place it creates a circle instead. I also spent lots of time making sure my bitboards are used properly as when I created the array last time I used it for more than I intended and lost most of the arrays of bitboards. I also noticed that the promotion GUI wasn’t properly centred and so fixed that. I don’t really like the lines as they use stipples instead of a solid colour and overall don’t look nice but tkinter doesn’t offer transparency apart from stipples and doesn’t allow lots of customisation of the arrows. Next I will likely try to add en passant."}, {"post_id": 33646, "ts": "2026-07-23T18:39:22Z", "hours": 4.0794, "text": "Finally, promotions! This was the last major part of chess that I didn’t have. I added it by finding the row of the piece when I move something. I hardcoded it so that it knows when the pawn is at the last row. When at the last row, it runs a choosePromotion() function. That function then creates a frame inside of my window. I then loop for each of the choices of the pieces I can choose. For each of these pieces I create a button with the piece (in the correct colour) in the centre. The frame waits until I pick a choice before adding a 1 on that piece’s bitboard at the correct location and removes the 1 on the pawn bitboard. Thats all I need to do to register a promotion! The rest of the time I spent optimising my code and realising that I didn’t have to use just a bitboard or an array, I could use both. Knowing that, I removed my getPiece() function and instead used an array to find the piece at a row and column. This is more efficient as arrays have a lookup time of O(1) whereas the getPiece() function has a lookup time of O(n) in the worst case. This is because my getPiece() function worked by cycling through each bitboard to find what piece was at a certain coordinate. This meant that I could also remove my totalPieces bitboard since this was only used in the getPiece() function to avoid cycling through all 12 bitboards if there was nothing there. Overall I’m really happy with how everything is going as I have all I really need for a proper game of 2 player chess. Next I am going to try and add the small things I am still missing such as En passant and the 50 move rule."}, {"post_id": 33388, "ts": "2026-07-23T09:22:55Z", "hours": 1.5583, "text": "Stalemates and three-fold repetition have now been added. Stalemates are calculated if legalMoves(turnColour) returns none but the king is not in check. I find if there is three-fold repetition by storing a hash of the board every move. This is stored in a dictionary as dictionaries are much better than arrays for lookups. After each move I see if that board state is in the dictionary. If it is I increase its value by 1. If not I set its value to 1. I then see how many times its reached that board state and if it is 3 times I end the game and show bright red text saying ‘Three-fold Repetition! Nobody wins!’. The hash includes piece positions, turn colour and castling rights as these are 3 of the 4 criteria that FIDE use. The fourth is en passant however that has not been added yet. Next I will add pawn promotions as this is the last major thing I don’t have."}, {"post_id": 33184, "ts": "2026-07-22T21:11:59Z", "hours": 2.3886, "text": "Castling! I’ve actually added castling this time. The castling also works with undo / redo move so that it undoes and redoes the move correctly. I’m starting to think my python file is getting a bit big now so I also created a few helper functions to help reduce code duplication. Its 572 lines currently. Luckily I am quite close to finishing the actual board and getting everything ready for a 2 person game of chess. This was my first goal so that the bot just becomes one of the players. Once I’m ready for a 2 person game of chess I’ll try to create another python file for the bot. This will help me keep track of what is doing what. Next I am going to add pawn promotion as that’s one of the biggest things that I don’t have yet. It is essential in late game and so is one of my priorities."}, {"post_id": 32902, "ts": "2026-07-22T11:18:43Z", "hours": 2.9108, "text": "Ever misclick and sell the game? Well not anymore! You can now undo moves by tapping the left arrow on the keybord and also redo those same moves if you change your mind by tapping the right arrow ! I did this by adding a saveMove() function which I run after every move I make. This saves the details of the last move to the moveHistory array: Piece Moved Start Position End Position Piece Captured (Just “” if None) Turn Colour Moves so far In my undoMove() function I remove the last move done and add it to an array called redoHistory . I then set all the details to the last entry in moveHistory . I do the same in my redoMove() function excpet I use the last entry in redoHistory instead. I’ve allowed undos after checkmate so that you can look for what went wrong and try other moves that might’ve stopped it. I also added sound! You’ll be able to see / hear all of this if the video uploads instead of just a screenshot. Next I am going to add castling (for real this time)."}, {"post_id": 32585, "ts": "2026-07-21T21:11:56Z", "hours": 2.6961, "text": "The game can now end! I have added a check to see if it is checkmate. When it is checkmate bright red text appears on the screen that says “Checkmate! WINNER wins!” (it says the colour which won not WINNER). I have also changed it so that when a player is in check they are forced to make a move that stops them from being in check. The only circles that come up are ones which would block the check to make it a bit easier for players. I’ve also made it so that my new function blockCheck() takes the calculations made by calculateLegalMoves() and uses it to display and allow only legal moves. As well as this anywhere that used to call calculateLegalMoves() now calls blockCheck instead. By making sure each function does 1 task it makes debugging easier ( not that anything would ever go wrong… ). Next I am going to try and add castling as this is a crucial mechanic that is used often."}, {"post_id": 32399, "ts": "2026-07-21T15:39:28Z", "hours": 2.8764, "text": "I’ve added better red and green overlays that are translucent so that you can see through them an easily know what piece you are taking. But most importantly, I’ve added check detection. I created a few new functions to do this. The first function I created was the isSquareAttacked(row, column, atkColour) function. It works by cycling through the bitboards and checking if they are atkColour or not. If they are find the type of piece and those pieces could attack the king. I think this will be helpful when I start to actually make the bot. I also created a simple findKing(colour) function to see where the king is so that I can parse that information into the isSquareAttacked() function. I then get a boolean. If it is true (the king is in check) I add the red overlay to the king so that the player easily knows they are in check but I don’t actually force them to block the attack yet. I think the overlay should be different but am not sure what to make it (gimme some ideas). I also created a slidingMoves() function as in my calculateLegalMoves() function I had ~40 lines of almost fully repeated code. Next I am going to force the player to block the check and end the game if it is checkmate."}, {"post_id": 32072, "ts": "2026-07-20T23:45:34Z", "hours": 0.9622, "text": "Finally took the time to change the piece textures so that black has an actual piece colour and you don’t get confused about whose pieces are whose"}, {"post_id": 31762, "ts": "2026-07-20T12:27:13Z", "hours": 3.4017, "text": "I’ve added dots to show where you can move when you click on a piece. If the position you want to move the piece to the dot is green but if it has an enemy piece the dot is red. Sadly, tkinter (the library I’m using) doesn’t support transparency. Because of this the dot takes up some of piece you want to capture. I can fix this by instead of drawing a circle drawing a translucent image… but tbh i rly cba. I will eventually do it to make it look better and actually add black pieces but i want to focus on getting things to actually work for now. The reason why this took me so long to implement is because as I was making the function to draw the dots I found that it was doing something very similar to my validMove() function but it was MUCH more efficient. In the old function I went through every square on the board and looked if the piece selected could get there. This is very inefficient and would build up quickly when I call the function 1000s of times per second (when I eventually add the bot). In my new function I look at the selected piece and have an array of each of the places it can move relative to its position. I then add the dots to those places if they are able to move there. This ended up with me just completely removing the old function and knowing its a valid move if there is a dot there. Next I am going to add something to see if the king is in check and maybe add some text when it is checkmate."}, {"post_id": 31082, "ts": "2026-07-19T06:07:07Z", "hours": 2.55, "text": "I’ve implemented bitboards instead of an array to store my board. At first I didn’t understand why bitboards were so good but after an amazing youtube video ( here ) I understand them and love them. I use them by having a 64 bit long integer (wow thats long) that represents the 8 by 8 board. Each bit can either be a 0 or 1 and represent whether there is a piece there or not. But you might think then how do you know what type of piece each 1 is? Thats why I use 12 bitboards (technically theres 1 more I added for extra efficiency but I don’t need that). Each of these 12 bitboards represents a type of piece in a specific colour (for example: white pawns and black pawns have 2 different bitboards). Then when I want to move pieces I create a position integer which is just 63 zeroes and a single 1 where the piece should be. I then invert this and use an AND statement so that all of the results for the AND are true where there are pieces apart from where I set the 1 at the start (though it is now a 0)."}, {"post_id": 30753, "ts": "2026-07-18T16:10:19Z", "hours": 3.6586, "text": "Added move legality. You can now mostly only make legal moves however I have not added something to checks if the king is in check. I made sure the move was legal by taking the startRow startColumn and endRow endColumn. I then look at what piece is being moved and ensure it is ok. If it isn’t nothing happens when you try to click and you have to make another move. I made sure that the correct colour moves as well. I also haven’t added pawn promotions, castling or en passant (tbh im not 100% certain on the rules of en passant). I couldn’t add castling as I don’t have a move history so would have no way of knowing if the king or rook have moved before. I’m now working on converting my current way of storing the board (a 2D array) into bitboards. After some research I found that bitboards are more than 5 times more efficient than arrays!"}, {"post_id": 30312, "ts": "2026-07-17T19:23:30Z", "hours": 2.0086, "text": "I can now move pieces! But there is no legality check yet. I also added a green outline if you select a piece. It works by listening for a click. Then when it gets a click it takes mouseX and mouseY and divides by positionSize to find which row and column it is on. It then checks against the board to see what is on that position. If it is a piece then it creates an outline and sets that as the active square. If it is empty AND active square has a value, it moves the piece."}, {"post_id": 30018, "ts": "2026-07-17T08:18:30Z", "hours": 1.4367, "text": "I’ve finally decided to actually making a chess bot and stick to it! So far I’ve set up the chess board for just visuals. Everything right now is purely visual. I am using python and the tkinter library."}]...
|
Title: Chess Bot by @GunRattler28 | Stardance
Dev Title: Chess Bot by @GunRattler28 | Stardance
Devlogs:
I’ve finally decided to actually making a chess bot and stick to it! So far I’ve set up the chess board for just visuals. Everything right now is purely visual. I am using python and the tkinter library.
I can now move pieces! But there is no legality check yet. I also added a green outline if you select a piece. It works by listening for a click. Then when it gets a click it takes mouseX and mouseY and divides by positionSize to find which row and column it is on. It then checks against the board to see what is on that position. If it is a piece then it creates an outline and sets that as the active square. If it is empty AND active square has a value, it moves the piece.
Added move legality. You can now mostly only make legal moves however I have not added something to checks if the king is in check. I made sure the move was legal by taking the startRow startColumn and endRow endColumn. I then look at what piece is being moved and ensure it is ok. If it isn’t nothing happens when you try to click and you have to make another move. I made sure that the correct colour moves as well. I also haven’t added pawn promotions, castling or en passant (tbh im not 100% certain on the rules of en passant). I couldn’t add castling as I don’t have a move history so would have no way of knowing if the king or rook have moved before. I’m now working on converting my current way of storing the board (a 2D array) into bitboards. After some research I found that bitboards are more than 5 times more efficient than arrays!
I’ve implemented bitboards instead of an array to store my board. At first I didn’t understand why bitboards were so good but after an amazing youtube video ( here ) I understand them and love them. I use them by having a 64 bit long integer (wow thats long) that represents the 8 by 8 board. Each bit can either be a 0 or 1 and represent whether there is a piece there or not. But you might think then how do you know what type of piece each 1 is? Thats why I use 12 bitboards (technically theres 1 more I added for extra efficiency but I don’t need that). Each of these 12 bitboards represents a type of piece in a specific colour (for example: white pawns and black pawns have 2 different bitboards). Then when I want to move pieces I create a position integer which is just 63 zeroes and a single 1 where the piece should be. I then invert this and use an AND statement so that all of the results for the AND are true where there are pieces apart from where I set the 1 at the start (though it is now a 0).
I’ve added dots to show where you can move when you click on a piece. If the position you want to move the piece to the dot is green but if it has an enemy piece the dot is red. Sadly, tkinter (the library I’m using) doesn’t support transparency. Because of this the dot takes up some of piece you want to capture. I can fix this by instead of drawing a circle drawing a translucent image… but tbh i rly cba. I will eventually do it to make it look better and actually add black pieces but i want to focus on getting things to actually work for now. The reason why this took me so long to implement is because as I was making the function to draw the dots I found that it was doing something very similar to my validMove() function but it was MUCH more efficient. In the old function I went through every square on the board and looked if the piece selected could get there. This is very inefficient and would build up quickly when I call the function 1000s of times per second (when I eventually add the bot). In my new function I look at the selected piece and have an array of each of the places it can move relative to its position. I then add the dots to those places if they are able to move there. This ended up with me just completely removing the old function and knowing its a valid move if there is a dot there. Next I am going to add something to see if the king is in check and maybe add some text when it is checkmate.
Finally took the time to change the piece textures so that black has an actual piece colour and you don’t get confused about whose pieces are whose
I’ve added better red and green overlays that are translucent so that you can see through them an easily know what piece you are taking. But most importantly, I’ve added check detection. I created a few new functions to do this. The first function I created was the isSquareAttacked(row, column, atkColour) function. It works by cycling through the bitboards and checking if they are atkColour or not. If they are find the type of piece and those pieces could attack the king. I think this will be helpful when I start to actually make the bot. I also created a simple findKing(colour) function to see where the king is so that I can parse that information into the isSquareAttacked() function. I then get a boolean. If it is true (the king is in check) I add the red overlay to the king so that the player easily knows they are in check but I don’t actually force them to block the attack yet. I think the overlay should be different but am not sure what to make it (gimme some ideas). I also created a slidingMoves() function as in my calculateLegalMoves() function I had ~40 lines of almost fully repeated code. Next I am going to force the player to block the check and end the game if it is checkmate.
The game can now end! I have added a check to see if it is checkmate. When it is checkmate bright red text appears on the screen that says “Checkmate! WINNER wins!” (it says the colour which won not WINNER). I have also changed it so that when a player is in check they are forced to make a move that stops them from being in check. The only circles that come up are ones which would block the check to make it a bit easier for players. I’ve also made it so that my new function blockCheck() takes the calculations made by calculateLegalMoves() and uses it to display and allow only legal moves. As well as this anywhere that used to call calculateLegalMoves() now calls blockCheck instead. By making sure each function does 1 task it makes debugging easier ( not that anything would ever go wrong… ). Next I am going to try and add castling as this is a crucial mechanic that is used often.
Ever misclick and sell the game? Well not anymore! You can now undo moves by tapping the left arrow on the keybord and also redo those same moves if you change your mind by tapping the right arrow ! I did this by adding a saveMove() function which I run after every move I make. This saves the details of the last move to the moveHistory array: Piece Moved Start Position End Position Piece Captured (Just “” if None) Turn Colour Moves so far In my undoMove() function I remove the last move done and add it to an array called redoHistory . I then set all the details to the last entry in moveHistory . I do the same in my redoMove() function excpet I use the last entry in redoHistory instead. I’ve allowed undos after checkmate so that you can look for what went wrong and try other moves that might’ve stopped it. I also added sound! You’ll be able to see / hear all of this if the video uploads instead of just a screenshot. Next I am going to add castling (for real this time).
Castling! I’ve actually added castling this time. The castling also works with undo / redo move so that it undoes and redoes the move correctly. I’m starting to think my python file is getting a bit big now so I also created a few helper functions to help reduce code duplication. Its 572 lines currently. Luckily I am quite close to finishing the actual board and getting everything ready for a 2 person game of chess. This was my first goal so that the bot just becomes one of the players. Once I’m ready for a 2 person game of chess I’ll try to create another python file for the bot. This will help me keep track of what is doing what. Next I am going to add pawn promotion as that’s one of the biggest things that I don’t have yet. It is essential in late game and so is one of my priorities.
Stalemates and three-fold repetition have now been added. Stalemates are calculated if legalMoves(turnColour) returns none but the king is not in check. I find if there is three-fold repetition by storing a hash of the board every move. This is stored in a dictionary as dictionaries are much better than arrays for lookups. After each move I see if that board state is in the dictionary. If it is I increase its value by 1. If not I set its value to 1. I then see how many times its reached that board state and if it is 3 times I end the game and show bright red text saying ‘Three-fold Repetition! Nobody wins!’. The hash includes piece positions, turn colour and castling rights as these are 3 of the 4 criteria that FIDE use. The fourth is en passant however that has not been added yet. Next I will add pawn promotions as this is the last major thing I don’t have.
Finally, promotions! This was the last major part of chess that I didn’t have. I added it by finding the row of the piece when I move something. I hardcoded it so that it knows when the pawn is at the last row. When at the last row, it runs a choosePromotion() function. That function then creates a frame inside of my window. I then loop for each of the choices of the pieces I can choose. For each of these pieces I create a button with the piece (in the correct colour) in the centre. The frame waits until I pick a choice before adding a 1 on that piece’s bitboard at the correct location and removes the 1 on the pawn bitboard. Thats all I need to do to register a promotion! The rest of the time I spent optimising my code and realising that I didn’t have to use just a bitboard or an array, I could use both. Knowing that, I removed my getPiece() function and instead used an array to find the piece at a row and column. This is more efficient as arrays have a lookup time of O(1) whereas the getPiece() function has a lookup time of O(n) in the worst case. This is because my getPiece() function worked by cycling through each bitboard to find what piece was at a certain coordinate. This meant that I could also remove my totalPieces bitboard since this was only used in the getPiece() function to avoid cycling through all 12 bitboards if there was nothing there. Overall I’m really happy with how everything is going as I have all I really need for a proper game of 2 player chess. Next I am going to try and add the small things I am still missing such as En passant and the 50 move rule.
Need to get your strategies into something you can see? Lines let you do just that. Right clicking and dragging draws a line that follows your cursor around. Right clicking again places the line. The lines are automatically centred so that they snap to the centre of the squares. If you line starts and ends at the same place it creates a circle instead. I also spent lots of time making sure my bitboards are used properly as when I created the array last time I used it for more than I intended and lost most of the arrays of bitboards. I also noticed that the promotion GUI wasn’t properly centred and so fixed that. I don’t really like the lines as they use stipples instead of a solid colour and overall don’t look nice but tkinter doesn’t offer transparency apart from stipples and doesn’t allow lots of customisation of the arrows. Next I will likely try to add en passant.
I was so unimpressed by how my old arrows looked I just completely switched libraries! I now use pygame instead of tkinter. It offers much more graphical customisability and now I can draw arrows (and other shapes) with true transparency instead of just stipples. One of the main downsides of pygame is that it constantly redraws each frame. This takes computing power and drains battery. To stop this I created a redraw boolean which tells my script when to redraw. This means instead of redrawing every single frame I redraw whenever something actually changes. Due to pygame I also customised the arrows so that the tails are rounded. This means that multiple arrows from the same point looks much cleaner. I also get true translucency instead of stipples and I added an icon!
En passant has been added. I find if it is legal by making sure the pawn capturing is on its fifth rank and the pawn being captured is moved 2 forward. Thats all I need for en passant (also only available for the first go it is there). I also made my program more efficient by ensuring I only rely on an array for finding what piece is on an index and drawing. Everything else uses bitboards which helps me fully utilise bitboards. I now have 2 things left: 50 move rule and Insufficient Material. Both of these end in a draw and are the last things I need for my program to have all the rules of chess! Next I will add the 50 move rule
Insufficient material and 50 move rule! These were the last things I needed to add before step 1 was complete. Now the next step is to actually add the bot! I’ve got everything I need down for a game of chess before 2 players. I added insufficient material by creating a new insufficientMaterial() . Inside the function are some if statements that determine whether winning is still possible (not assisted by opponent going into the corner). I also added the 50 move rule by checking moves since last pawn advancement or capture and if it exceeds 50 then end the game. I also made it so that once the game ends you can go undo moves and continue playing instead of just having the game end. Next I am going to try to clean up my code, split the code into multiple files and optimise it.
I’ve split my file into 5 now. There is moveGeneration.py which calculates all the legal moves, moveExecution.py which actually moves the pieces, updateBoard.py which updates the board, gui.py which handles graphics (like arrows, promotion page and clicks) and main.py which says what functions should happen when an input is given and has a while loop for while the game is running (as is needed by pygame). Splitting the file makes it much easier to debug, keep track and add features instead of just having one ~1000 line file where everything is jumbled up. Next I am going to start work on the actual bot.
I’ve added a bot! The main point of this was to link the new file I made ( bot.py ) to main.py . The bot currently just chooses moves based off of what would result in the best material difference between white (player) and black (bot). It calculates these moves with a depth of 4. Since the main point wasn’t to actually make a bot in one go the bot isn’t very good currently. However I still added alpha beta pruning. Alpha beta pruning works by storing the best guaranteed outcome for you (alpha) and the best guaranteed outcome for the opponent (beta). Whenever it finds a path where the opponent can force a move that is worse than the alpha, it immediately stops going down that path. The same happens for beta. I also removed some duplicated code in main.py and fixed the undo and redo move logic as I forgot to check if it still worked properly after switching from tkinter to pygame. Currently you can only tap the buttons and but can’t hold to go back multiple moves. Undoing and redoing moves with a bot was a bit confusing for me and so I decided to make it so that when undoing and redoing there is 3 seconds before the bot moves. During these 3 seconds you can go forwards or backwards to reset the timer. I used threading so that the bot starts to calculate what move it would make at the same time as the 3 seconds are passing. This means that if you decide to stay on the turn you don’t need to wait as long. I also blurred the background of the the promotion GUI so that it is grabs the players attention. Next I am going to try and add a better bot by assigning each possible move a score that is based off of the squares it controls, offensive and defensive opportunities and potentials for forks (when 2 pieces are being attacked by 1 piece so only one piece can move out of danger).
I’ve added position bonuses! I created a new file which rewards moves which place certain pieces in certain areas. For example pawns are rewarded for controlling the centre and pushing to the 7th rank (so that they can promote). The bot now tries to control squares and this means it now has proper openings. I also completely updated the file structure to split the responsibilities more clearly. I also updated main.py and logic.py and made them classes. Making logic.py a class means that I can use it for both the player actions and bot actions without them interfering. This also makes my previousMove() much cleaner as it means when the bot uses it, it doesn’t interfere with when the player uses it. Due to the new file structure it means that I can easily change the weights of each of the different modules to change how the bot plays easily. Later I will add a slider or at least a setting that lets the player control these modules. As well as this I also changed it so that drawing the same arrow again removes the arrow. Next I am going to try add another module that rewards moves which keep the king safe.
Colour randomisation! The colour you play as is chosen at random at the start of the game. The board is also automatically rotated so that you are at the bottom and the bot is at the top. This also helps to show which colour you are playing as. I also show the last move by making the starting square #97C997 and the ending square #8DCE8D. I did this as when I asked I watched someone play it I noticed that they would often undo the move, to see what the bot just did, then redo the move again. This helps make everything feel more polished. I like the ending square colour ( #8DCE8D ) but I’m not sure if I like the starting square colour ( #97C997 ). These colours are very easy to change though and so I can quickly edit them to test out different colours. I also realised that when I eventually ship the project most people who download it won’t have the font I use ( dynapuff ) installed. I tested it out to see what it is like without it and found that it crashed. To fix this, I implemented a try - except statement which sees if dynapuff exists on the user’s computer. If it does then it uses dynapuff which is how it is supposed to be played but if they don’t have the font it uses arial instead. Due to the strange spacing of dynapuff I used double spaces between the words on the game over messages, but when using arial the double spaces look quite strange. I also tweaked some of the position bonus values to further encourage castling. Next I am going to try to add a file to reward moves which keep keep the king safe.
Better arrows! I don’t remember where but there is one place where drawing arrows in a knights movement creates an L shaped arrow instead of just a normal arrow. I love this and so implemented it into my program. I also added aspiration windows. Aspiration windows work on the basis that moves don’t usually swing the score by a lot. This means that when going down paths we can set the lower and upper bounds to previous score ± window size. This helps prune more paths. If we find that the score is actually higher than we expect we search again but without the aspiration window. This helps remove lots of the obviously bad paths which makes the program much more efficient. Next I am going to add zobrist hashing so that I can use transposition tables.
Improved efficiency! I’ve improved the efficiency of the bot by sorting the possible moves better. This is useful as it means when the alpha beta pruning goes over everything it searches the best trees first. This means that it can prune more branches. I also removed some random integer conversions on my bitboards as they did nothing, since the bitboards are already integers. I also removed a restriction that didn’t let the player do anything on the bot’s turn. Now the player can draw during the bot’s turn. I also went across all of my code and changed it to use (row, column) instead of (column, row). This helps keep things cleaner. I then also changed the bot calculations from from using the fullyLegalMoves() function to the movementLegalMoves function(). fullyLegalMoves() just makes sure that the king isnt in check after the move. If it is then it undoes the move. By insteading doing this in my bot.py it means I can just not undo the move and immediately remove that move. This means that I don’t need to undo the move more times than needed. This is very helful as undoing a move takes up computing power. I also completely replaced the piece names with integers as this would be slightly better and I could use bit operations with integers. As well as this I also added a timer that shows how long the bot took to make its move. By adding all of these changes the bot reduced its time on the first move as white from ~4 seconds (with depth 4) to 3 seconds (with depth 5). This shows that it is much more efficient as, time taken increases exponentially as depth increases. As well as this 3 seconds is the earliest the bot can move and so the true speed is likely much less.
Transposition tables! I’ve added zobrist hashing so that I can create a dictionary that contains all of the board states I have already evaluated. Since many different moves can lead to the same position it means that I can save computing power by just getting the score of that board state instead of having to re-evaluate the board again. Zobrist hashes are just a way to represent the board in hash. This is useful as I can use them as the keys in my dictionary and quickly see if the current board state has been evaluated yet. I also fixed a bug that let the bot play illegal moves as there wasn’t a legality check after the aspiration window failed. An abort flag was also added that is set to True when the user undos or redos a move. This lets me instantly stop that thread’s search for the best move ensuring it doesn’t use unneccessary computing power. Next I am going to try and implement iterative deepening so that I can force the bot to move within a certain amount of time. This would let me make the bot play different ‘time controls’ by altering how long it has to think about what move it should play. I tested it against some bots from chess.com and it managed to beat Wally (a 1800 bot) but lost to Li (a 2000 bot)
Iterative deepening! The bot now searches depths in an ascending order instead of descending. This is only possible thanks to my transposition tables but means that I can give the bot a time limit. Once the time limit is reached it just return the best move of the highest depth its fully searched. This means that I can search to different depths in different positions whilst making sure I always play within the time limit. This is good as it means I can have a high depth during the opening and endgame whilst having a medium depth during mid game instead of having a medium depth everywhere to ensure moves at midgame don’t take too long. Midgame usually has a lower depth due to the high number or possible moves that can be played. I can also easily change the time limit with just a single variable. This means I can get my bot to play faster but also worse or take longer but play better. I added incremental evaluation. This means that for each move, instead of looping through all 12 bitboards and recalculating the evaluation score from scratch I instead update it from the current evaluation score. So when I move a piece I subtract its old it’s material value and then add the material value if it still exists. The same happens for the position bonuses. This takes my time complexity from O(n) to O(1) which is a massive upgrade. These changes lets my depth in early game and endgame almost always stay at 7 whilst during midgame hover between depth 6 and depth 7. Currently I determine if I am in endgame if both queens are gone and the total amount of pieces is below 16 however I feel that this isn’t a good measure and I should improve this. Next I will try to improve efficiency by improving my move ordering so that the alpha beta pruning is more efficient.
I’ve added a better endgame condition! Now endgame is determined if (there are no queens and less than 16 pieces on the board) or if there is less than 8 pieces on the board. This is better because it means when pawns promote the king doesn’t run to the corners or use the other position bonus table. I also improved my bot’s 3 fold repetition check when searching for moves by using a dictionary with hashes as keys and number of times they have appeared as values. This lets me check if I have been in the position 3 times before in O(1) instead of O(n) . I also improved my makeMove() and previousMove() functions by checking if its the bot searching for moves and if it is then only storing the key information instead of everything. To help simplify previousMove() I instead created a seperate function called unmakeMove() (clearly related to makeMove() ) to undo moves when the bot is searching. I also changed the time the bot has to move from 3 seconds to 1.5 seconds so that it feels snappier. Probably cos I have 0 patience when I want to test if the bot works :). Next I am going to try and make my bot faster by improving the move ordering so that alpha beta pruning can be more effective.
I’ve improved my endgame condition. When testing the bot I found that during endgame it would sometimes move its king to the corners of the board. I realised that this was because it saw a queen on the board and due to the poor endgame condition it thought this meant that it wasn’t endgame and instead used the early / mid game position bonus table which rewards staying safe in the corners. However in endgame moving the king to the corner isn’t good as it makes checkmates easier for the opponent and means that the king isn’t used to help checkmate the opponent, which is crucial in certain endgames such as a king and queen against just a king. In situations like this the king is necessary in achieving a checkmate. I changed it to being if there is less than (2 black queens and 2 white queens and less than 16 pieces) or if there is less than 5 pieces not including pawns. This is more accurate and ensures the king actually helps win and tries to stop a checkmate on itself. I also improved my zobrist hashing by fully incrementing it. Previously I incremented it for everything except castling and en passant however now these are implemented as well. I also noticed that the bot would walk its king towards the centre of the board during the opening which puzzled me until I read through my code and realised its because I set totalPieces to 0, asked if it was endgame, and afterwards set totalPieces to the true value. This was pretty difficult to find as I couldn’t see it by just seeing if it thought it was endgame at the end of a move. This makes sure I can actually get my hashes in O(1) time. Next I am going to try and make my arrows darker when over each other.
I’ve massively improved my organisation! I’m now properly utilising branches which is allowing me to work on multiple changes at the same time. Currently I am working on 2 branches. One is about improving my maintainability by adding comments so that other people can understand what I am doing and why. This also has the added benefit of me being able to make some small improvements and bug fixes as I am reading through all of my code. The second branch is about move ordering which is super important as it makes my alpha beta pruning more effective. Working on these changes on branches is useful because it not only keeps it organised and easy to see what each commit is for but also means that if I decide I don’t want the change anymore it is easy to remove as it isn’t mixed in with all the other changes. To help with this I’ve also created a Jira board which helps me keep my ideas organised. My branches are called SCRUM-NUMBER/NAME . The scrum number is given to me by Jira. As well as improving my organisation I also further improved my zobrist hashing by replacing using modulo on the hash to get the index of the transposition with using an and gate between my hash and the table size - 1 (which is just all 1s and a single 0 on the left most digit since the table size is a power of 2). I also stored my castle rights as a single 4 digit binary digit where each digit represents whether a certain rook has castling rights or not. My logic with this was since the point of bitboards is 12 integer is better than 64 integers, a single 4 digit integer is better than a dictionary of 4 values but I am not actually sure if this is true or not. On the maintainability branch so far I have added comments to all of main.py . On the move ordering branch I have improved my sorting from just sorting by most valuable capture and least valuable attacker which is only good for captures to also scoring promotions very highly. I also instantly push the previous best move to the front of the queue and score moves which cutoff branches in alpha beta pruning highly as well. Next I will try to add comments to one of my other files and improve my move ordering by not using lambda functions.
Move ordering improved! I’ve merged my SCRUM-29/move-ordering branch with main and now have my improved move ordering in main. This has let me go from a depth of 5 to almost always a depth of at least 6. I then merged main into SCRUM-42/improve-maintainability so that I could add comments to the code I added from the move ordering branch. Next I am going to add some comments explaining what things do.
Massive improvements in depth! I added 2 changes: null move pruning and late move reduction. Null move pruning is when the bot ‘passes its turn’, when searching for the best move. The bot then evaluates the board at a reduced depth after letting the opponent play twice in a row. If it is still in a winning position then that means it is winning by a lot. This means that it can prune this branch since the opponent wouldn’t let the game get to this state and would make a better decision earlier in the tree. Since the opponent wouldn’t let the game get to this it means that further analysis isn’t necessary. Late move reduction works on the basis that I have good move ordering, which I just worked on so I would hope I do. Late move reduction means that I search the later moves at a reduced depth since the best moves are at the start and so later moves are likely bad. If they turn out to be good after the reduced evaluation depth then they are researched at the full depth. This helps reduce the amount of moves which need to be evaluated by a lot. Together null move pruning and late move reduction have taken my depth from around 6 - 7 in midgame to around depth 7 - 10 in midgame! In endgame this is even more. When I tested it in endgame it got to a depth of 13! My next change is something called quiescent search which will boost the bot’s elo by massive amounts as it should mostly stop the bot from making blunders.
Quiescent search is amazing! After adding it my bot instantly became better. A quiescent search just means that when evaluating a board state to a certain depth even if you reach that depth you need to evaluate until a quiet move (a move that isn’t a capture). This means that even if the bot reaches the max depth in the middle of a piece trade it won’t just think the move is amazing since all it can see it won a piece. Since I added the bot, when tested against other bots it has made 0 blunders! In fact it beat Li, a bot rated 2000 on chess.com ! I’m really proud of this and think it is really cool to say that my bot has beaten a 2000 rated bot. However, it only beat the bot once and so it could just be lucky. I was going to test it in 2 more games against the same bot but found myself getting bored of manually inputting moves into chess.com and my bot to get them to play each other. Therefore, I’m planning on making my bot able to play other bots on its own. To do this I will need to download the other bots and make my bot give properly formatted inputs and outputs.
I’ve added FEN strings! FEN strings (Forsyth-Edwards Notation) are a way to store positions of chess games. They are used by many chess engines to allow players to start from different positions or midway through games. Adding these wasn’t too difficult as the strings are formatted in a nice way. I simply split up the FEN string into its 6 parts and set my variables to the value in the FEN string. To test if it worked I inputted the FEN string 4kb1r/p2n1ppp/4q3/4p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 1 16 which gives the position of the famous opera game directly before the queen sacrifice. I then set the bot colour to white and was glad to see that it immediately found the best move (the queen sacrifice). I also added premoves so that I can choose my move in advance. I did this by just storing the move I make when it is the bot’s turn and playing it as soon as the bot plays. However I can only premove 1 move ahead. Next I will try to allow the user to premove multiple moves ahead.
Way better premoves. I’ve updated my premove system to store multiple premoves. I did this by storing eaach premove as a tuple of ( startRow , startColumn , endRow , endColumn ). These tuples contain all the data I need to know and can be easily appended into an array. To clear a premove you can either do the same move again which removes that specific premove or tap the middle mouse button anywhere which clears all premoves and arrows. I also render a translucent version of the piece that I’ve moved to its premove destination and remove the texture for where it was originally. This helps you keep track of what your premoves are actually doing. I do this by drawing the board normally then looping through each move in the premoves array and drawing that piece at its correct destination in reduced alpha. Next I am going to try to improve my bot further so that I can push to a depth of around 10 during midgame.
Premoves finished! My premoves are now fully implemented and the branch is merged. Making the move with middle click removes that specific premove. Middle clicking a random square clears all premoves and arrows. I also added principal variation search (pvs) which is just searching moves that aren’t the previous best move with a tight move score boundary then researching them normally if they are better than expected. I am probably going to ship soon and so will start polishing the project up now.
I’ve made a few small changes that help slightly speed the bot up. I stopped checking whether I have surpassed the time limit every move I search and instead every 2048 moves I search. This is better as it removes thousands of unnecessary library calls which each add a small amount of time to how long each move takes to search. Since computers are very fast 2048 doesn’t actually take long at all but it massively reduces the amount of times the pygame library is called. I also changed my move ordering to sort manually instead of with in a lambda function since this lambda functions are more inefficient. I am next going to ship the project!!!
Thanks for all the positive feedback and constructive criticism on my ship! Working to improve where people said and I am starting by trying to add comments to my code to explain what is happening. So far I’ve fully commented 3 / 7 files and am around 30% through commenting logic.py which contains most of the complicated code. Its taking a while but I really hope that this will help people understand what I am doing and that they will like my project more because of this. Next I am going to continue adding comments to logic.py and will probably have to make a devlog after just that since the file is pretty big and has slightly complicated stuff in it. Once again thanks to all the people who rated the project or gave feedback!
Yup! I did what I said I would last time, and finished commenting ALL of logic.py . I don’t think I can describe how long this took and how annoying it was to have to go through 750+ lines of code and add comments explaining things. And then I realise that there’s just random stuff that I don’t even need. Anyway, I’m pretty satisfied that I managed to comment so much. This is definitely the largest file and so I’m happy I’ve gotten it out of the way. There’s not really much I can’t say like explaining and stuff because all I’ve done for the last 5 hours is explain stuff. Anyway once I’ve commented everything, I’ll probably make another devlog and then go and work on something else.
I started getting bored of just commenting my code so I created a new branch in which I am trying to make my bot be able to play other bots. After some research, I found that the engine needs to be able to take in moves in the form of algebraic notation (a type of shorthand notation for moves. for example: Be4). Because of this I read a Wikipedia page on algebraic notation and made a function to convert between it and the parameters my makeMove() takes, that being: startRow , startColumn , endRow and endColumn . I have done this by first checking if the move is a castle (notated by O-O or O-O-O) then forming the full start and end coordinates by hardcoding the column values and finding the row based off the colour that is castling. Then I find what piece is moving (shown by the first letter) and if there is a promotion (shown by ‘=’ sign). Algebraic also has characters to show captures (x), checkmates (#) and checks (+), however the engine does not need these and so they are removed in 3 separate while loops. At this point I am left with just the end coordinates and maybe a combination of start coordinates. To find the full start coordinates (as they are all I am missing) I loop through the specific bitboard for the piece and whenever I find a square where there is a piece, I check whether it matches with the start coordinates I have, and if it does whether it can legally reach the end square. If it can then that is my piece and I return its start coordinates and the end coordinates it moves to (which is given in the input string). Currently, this function lives inside a random file in my project folder but my next step will be to properly integrate it into the rest of the code.
So, I’ve actually decided to work on making my bot playable on a website since that was one of the main things people said they would have wanted last ship and stardance is ending soon. But thats not why I’m posting. I’m posting because I spent the last 40 minutes rooting out a bug that caused my premoves to not be played. My first step was playing the release version that can be downloaded to see if the bug was there as well. Luckily it wasn’t so I went to my current commit on my main branch and kepy undoing commits (and saving them with git stash ) until the bug was gone. I then found that the bug only became a thing after I merged my commenting branch. Because of this I did the same thing of undoing commits and saving them on the commenting branch til I found the commit that caused the bug. I then went through all the changes until I found the problem. Then I fixed the bug, merged the fix to main then merged main into my other branches so that they would have the bug fix as well. I’ve already done some research and have found that pygbag seems to fit my needs for turning my pygame program into a website pretty well. Due to this I will probably use pygbag to get a website version of my chess bot up and running! Attached is a screenshot of the commit that caused the bug. Before the commit is on the left and after the commit is on the right. Try to find what caused the bug! If you can’t check the comments of the post as I am gonna say what the problem was there. I also attached a photo of how my git tree looks now and… it looks sooo messy lol.
Ships:
I’m finally shipping the chess bot! So excited to see what people think of it :) . Before you try my project I highly suggest reading the README (on my github page https://github.com/GunRattler28/Chess-Bot ). It has a full list of all controls which will help you understand how to play. Anways, can’t wait to see what you guys think of it!...
|
1792856898.3795884
|
1793720898.3795884
|
|
53406
|
Iris by @Hamna | Stardance
|
Hamna
|
1
|
144.68
|
1521
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-18T22:02:27Z", "hours": 131.0, "multiplier": 13.36, "payout": 1521, "blessing": "neutral", "text": "This is my booking website for wellness (my family hotel). There’s a public site, online checkout, a Reception console and a Management console The most difficult part to stop double bookings btw the website and the front desk,I fixed this by making a hold time of 10 mints on suites I’m really so proud of this project as this was something I was planning to make for my family and it turned out to so amazing and I got to learn so much about security (P.S , This is my last project on Hackclub , I really had the cool experience here, This platform really motivated me to make this project)", "repo": "https://github.com/lilithCode/Iris_ChatBot", "demo": "https://iris-chat-bot-lac.vercel.app/"}]...
|
[{"post_id": 76064, "ts": "202 [{"post_id": 76064, "ts": "2026-10-02T07:21:18Z", "hours": 6.8922, "text": "Changed the UI a bit"}, {"post_id": 73916, "ts": "2026-09-26T09:52:32Z", "hours": 2.9411, "text": "There were some UI erros in the git pages file i made, so this is the last final verison for this project for the demo link: hope you’ll like this : )"}, {"post_id": 71239, "ts": "2026-09-20T14:41:31Z", "hours": 3.8447, "text": "Fixed it today! I replaced ChatInterface in the Space with a gr.Interface that takes the history as a JSON input, so the conversation actually reaches the model now. I also added a history count to the logs so I can see it arriving with every message. This fixes Amadeus too, since they share the same Space. Iris finally remembers what we’re talking about, and that feels like the right place to call this project complete"}, {"post_id": 70295, "ts": "2026-09-18T21:53:46Z", "hours": 4.7014, "text": "Spent today debugging the memory problem. The frontend was sending the chat history and the router was clearly using it, but the model was answering like every message was the first one. I asked “what did I just ask?” and got a totally random reply. Then I checked the Space’s API info and found the problem: Gradio 6’s ChatInterface hides the history from the API, so it was silently throwing the whole conversation away"}, {"post_id": 69607, "ts": "2026-09-17T12:20:33Z", "hours": 5.0708, "text": "Tested the new pipeline today. I asked “who won the last cricket world cup?” and it searched, found the facts and answered “Australia” correctly. Iris knowing current events feels like a huge upgrade. But while chatting normally I noticed something weird: it kept saying “Hai haii” to everything and had no idea what we’d been talking about"}, {"post_id": 69182, "ts": "2026-09-16T15:25:12Z", "hours": 4.86, "text": "Big backend day. I made Iris use the exact same backend as my other project, Amadeus: the same Hugging Face ZeroGPU Space, plus a RAG pipeline. Now a small router model on Hack Club AI rewrites the question, decides whether it needs a web search, and remembers facts about the user. If it needs current info, it searches the web with Exa and passes verified facts to the model. The UI stayed exactly the same"}, {"post_id": 67350, "ts": "2026-09-13T13:00:04Z", "hours": 8.9375, "text": "Tried Modal as another hosting option today, to compare it with ZeroGPU. I switched the route to call a Modal endpoint with an API key. It worked, but managing two setups for two projects (Iris and Amadeus) was getting messy, and I started thinking about putting both on the same backend"}, {"post_id": 66168, "ts": "2026-09-11T20:25:17Z", "hours": 11.0492, "text": "Came back to Iris after a while because responses were way too slow on free CPU hosting. Today I moved the model to Hugging Face ZeroGPU, so it actually runs on a GPU now. I rewrote the backend route to call the Space through the Gradio API, with retries and fallback messages in case something fail"}, {"post_id": 65771, "ts": "2026-09-11T04:17:48Z", "hours": 6.2706, "text": "Final UI touches today. I polished the About modal, updated the project links and rewrote the README with the whole training pipeline. Then I deployed it on Vercel. Iris is officially live and I shared the link with everyone"}, {"post_id": 65244, "ts": "2026-09-10T07:34:11Z", "hours": 8.7197, "text": "Today I connected the frontend to the real Hugging Face backend instead of the placeholder. Seeing Iris reply on my own website, in Senpai’s style, felt unreal. Responses were slow, but it was talking, and I spent way too long just chatting with it"}, {"post_id": 64336, "ts": "2026-09-08T17:12:01Z", "hours": 3.4358, "text": "I replaced the old playlist with songs that match the vibe better and made new covers for each track. Some of the song links broke right after, so I fixed those, and one mp3 was way too big, so I compressed it. Small stuff, but the radio station panel feels complete now"}, {"post_id": 64109, "ts": "2026-09-08T09:37:59Z", "hours": 4.6706, "text": "I didn’t like how the UI looked yesterday, so today I redesigned almost all of it. I went with a soft lavender pastel theme that feels more like Iris, removed the cyber cursor and added a cute Senpai avatar that pops up with little messages. I also made the whole thing responsive so it works on phones too"}, {"post_id": 63712, "ts": "2026-09-07T17:27:59Z", "hours": 9.7992, "text": "Started the frontend today with Next.js and Tailwind. I built the main layout: a chat panel in the middle, a music player on the left, and a side panel on the right. I also added a loading screen and little send/receive sound effects, because a chatbot without sounds feels lifeless. Pushed the first commit with the UI, routing and model connection all together"}, {"post_id": 62439, "ts": "2026-09-05T19:40:06Z", "hours": 11.1208, "text": "Training ran for 1000 steps today and I watched the loss drop from about 2.5 to 0.3. When I tested it, it replied with “Kk” and an eye roll emoji, and that’s literally how Senpai texts. I sent screenshots to my friends and they were all shocked. That was the best moment of the project so far"}, {"post_id": 61489, "ts": "2026-09-04T13:05:44Z", "hours": 22.205, "text": "Set up my Kaggle notebook today with Unsloth and Llama-3 8B as the base model. Most of the day went into fighting configs and memory errors on the T4 x2 GPUs. After a lot of trial and error I settled on a 2048 sequence length and a 5e-5 learning rate. It finally started training without crashing, and I almost cried"}, {"post_id": 59161, "ts": "2026-08-31T19:49:02Z", "hours": 12.4886, "text": "Today I turned the cleaned chats into proper training data. Senpai sends five short messages in a row instead of one long one, so I grouped consecutive messages together to keep that natural texting flow. I ended up with 12,613 dialogue samples. Seeing that number made it feel like a real project for the first time"}, {"post_id": 56717, "ts": "2026-08-28T16:11:34Z", "hours": 7.4811, "text": "Spent the whole day cleaning the WhatsApp export. It was around 81,000 lines of pure chaos: timestamps, “media omitted”, deleted messages, random links. I wrote a script to strip all the junk out and kept only the actual conversations. My eyes hurt, but the data finally looks usable"}, {"post_id": 54957, "ts": "2026-08-26T12:02:23Z", "hours": 9.8514, "text": "Today I finally started something I’d been thinking about for a while. Every AI chatbot I’ve used feels so polite and robotic, like it has zero aura. So I decided to make one that talks like a real person, specifically my friend “Senpai”. I exported our WhatsApp chats and just stared at them for a bit, wondering what I’d gotten myself into"}]...
|
Title: Iris by @Hamna | Stardance
Devlogs:
Today Title: Iris by @Hamna | Stardance
Devlogs:
Today I finally started something I’d been thinking about for a while. Every AI chatbot I’ve used feels so polite and robotic, like it has zero aura. So I decided to make one that talks like a real person, specifically my friend “Senpai”. I exported our WhatsApp chats and just stared at them for a bit, wondering what I’d gotten myself into
Spent the whole day cleaning the WhatsApp export. It was around 81,000 lines of pure chaos: timestamps, “media omitted”, deleted messages, random links. I wrote a script to strip all the junk out and kept only the actual conversations. My eyes hurt, but the data finally looks usable
Today I turned the cleaned chats into proper training data. Senpai sends five short messages in a row instead of one long one, so I grouped consecutive messages together to keep that natural texting flow. I ended up with 12,613 dialogue samples. Seeing that number made it feel like a real project for the first time
Set up my Kaggle notebook today with Unsloth and Llama-3 8B as the base model. Most of the day went into fighting configs and memory errors on the T4 x2 GPUs. After a lot of trial and error I settled on a 2048 sequence length and a 5e-5 learning rate. It finally started training without crashing, and I almost cried
Training ran for 1000 steps today and I watched the loss drop from about 2.5 to 0.3. When I tested it, it replied with “Kk” and an eye roll emoji, and that’s literally how Senpai texts. I sent screenshots to my friends and they were all shocked. That was the best moment of the project so far
Started the frontend today with Next.js and Tailwind. I built the main layout: a chat panel in the middle, a music player on the left, and a side panel on the right. I also added a loading screen and little send/receive sound effects, because a chatbot without sounds feels lifeless. Pushed the first commit with the UI, routing and model connection all together
I didn’t like how the UI looked yesterday, so today I redesigned almost all of it. I went with a soft lavender pastel theme that feels more like Iris, removed the cyber cursor and added a cute Senpai avatar that pops up with little messages. I also made the whole thing responsive so it works on phones too
I replaced the old playlist with songs that match the vibe better and made new covers for each track. Some of the song links broke right after, so I fixed those, and one mp3 was way too big, so I compressed it. Small stuff, but the radio station panel feels complete now
Today I connected the frontend to the real Hugging Face backend instead of the placeholder. Seeing Iris reply on my own website, in Senpai’s style, felt unreal. Responses were slow, but it was talking, and I spent way too long just chatting with it
Final UI touches today. I polished the About modal, updated the project links and rewrote the README with the whole training pipeline. Then I deployed it on Vercel. Iris is officially live and I shared the link with everyone
Came back to Iris after a while because responses were way too slow on free CPU hosting. Today I moved the model to Hugging Face ZeroGPU, so it actually runs on a GPU now. I rewrote the backend route to call the Space through the Gradio API, with retries and fallback messages in case something fail
Tried Modal as another hosting option today, to compare it with ZeroGPU. I switched the route to call a Modal endpoint with an API key. It worked, but managing two setups for two projects (Iris and Amadeus) was getting messy, and I started thinking about putting both on the same backend
Big backend day. I made Iris use the exact same backend as my other project, Amadeus: the same Hugging Face ZeroGPU Space, plus a RAG pipeline. Now a small router model on Hack Club AI rewrites the question, decides whether it needs a web search, and remembers facts about the user. If it needs current info, it searches the web with Exa and passes verified facts to the model. The UI stayed exactly the same
Tested the new pipeline today. I asked “who won the last cricket world cup?” and it searched, found the facts and answered “Australia” correctly. Iris knowing current events feels like a huge upgrade. But while chatting normally I noticed something weird: it kept saying “Hai haii” to everything and had no idea what we’d been talking about
Spent today debugging the memory problem. The frontend was sending the chat history and the router was clearly using it, but the model was answering like every message was the first one. I asked “what did I just ask?” and got a totally random reply. Then I checked the Space’s API info and found the problem: Gradio 6’s ChatInterface hides the history from the API, so it was silently throwing the whole conversation away
Fixed it today! I replaced ChatInterface in the Space with a gr.Interface that takes the history as a JSON input, so the conversation actually reaches the model now. I also added a history count to the logs so I can see it arriving with every message. This fixes Amadeus too, since they share the same Space. Iris finally remembers what we’re talking about, and that feels like the right place to call this project complete
There were some UI erros in the git pages file i made, so this is the last final verison for this project for the demo link: hope you’ll like this : )
Changed the UI a bit
Ships:
This is my booking website for wellness (my family hotel). There’s a public site, online checkout, a Reception console and a Management console The most difficult part to stop double bookings btw the website and the front desk,I fixed this by making a hold time of 10 mints on suites I’m really so proud of this project as this was something I was planning to make for my family and it turned out to so amazing and I got to learn so much about security (P.S , This is my last project on Hackclub , I really had the cool experience here, This platform really motivated me to make this project)...
|
1792856876.8815353
|
1792943276.8815353
|
|
46051
|
Tic-Tac-Cube by @EthanCubes | Stardance
|
EthanCubes
|
1
|
145.0
|
0
|
[{"ship_num": 1, "ts": "2026-1 [{"ship_num": 1, "ts": "2026-10-18T21:33:24Z", "hours": 145.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "I made a tic-tac-cube game where you can rotate the board like a Rubik’s cube! It was made in C++ with Raylib without a game engine, and only uses 2 libraries, Raylib, and Nlohmann-JSON. I’m really proud of the way I coded the entire GUI, buttons, board, and bot. Features Tic-Tac-Toe game on a Rubik’s cube Singleplayer mode against computer Local multiplayer against another player (or yourself) Main menu & help page. I coded the bot to be somewhat intelligent, and will actively try to block your attacks and create it’s own ones. It can turn the cube just like you, and will rotate the cube to get out of a pickle. This is my first C++ project, and my first experience with languages that have to be compiled before running. It was honestly really fun to make. I put a LOT of effort into this game, over 140 hours.", "repo": "https://github.com/EthanCubes/tic-tac-cube", "demo": "https://ethancubes.itch.io/tic-tac-cube"}]...
|
[{"post_id": 80806, "ts": "202 [{"post_id": 80806, "ts": "2026-10-18T20:46:55Z", "hours": 0.6053, "text": "Final Devlog Before Ship I’m done stalling. Every time I’ve shipped a project for Stardance, I’ve stalled for like a week trying to make sure it’s good enough. This time, I’ve stalled for more than a month. I’ve even shipped 2 separate projects to different Hack Club events in the time that this project has technically been “good enough”. Essentially, I just worked on the README a bit more. I did manage to fix a few errors. For example, I had a few spelling and grammar errors. I also added a bit more detail to the readme."}, {"post_id": 77976, "ts": "2026-10-09T02:51:47Z", "hours": 1.4722, "text": "Devlog #38 (25c796f): the beans aren’t going to know what hit them (regarding my 145 hours waiting to be shipped) I made a couple of changes, such as improving the readme and disabling the exit button, greying it out and making so that it can’t be pressed. For context, the game can only run on the web, and the exit button just doesn’t do anything useful on the web. I also customized the itch.io page (which hasn’t been released yet but you can access it here via secret url). It looks kinda decent (i just copied the game’s color scheme)"}, {"post_id": 76564, "ts": "2026-10-03T23:45:12Z", "hours": 2.1303, "text": "Devlog #37 (8b55316): I made a web build for the game! I took a break from Stardance to make something for Out to C, and when I was making a C game with Raylib I discovered that it is extremely hard to make a downloadable executable run well on Windows or MacOS, because on Windows you have to include DLL files, and on MacOS it’s just harder to install Raylib. In the end, I ended up making a web build, and since I wasn’t so bad, I decided to make a web build for this. I used Emscripten, and using the em++ tool with the asyncify option, manage to get most of the game logic working, with only one small frame generation bug, which I fixed by changing this_thread::sleep for to something that is easier for the browser to render. I also made some other QoL changes so that the bot feels less stagnant, hopefully."}, {"post_id": 71599, "ts": "2026-09-21T03:57:04Z", "hours": 2.0317, "text": "Devlog #36 (fcc4f2f): I made the first gameplay change in a while, and also changed the theming a bit more. The gameplay changes I noticed that the game was getting a bit… repetitive, so I changed the bot to have an element of randomness when there are multiple spots that provide similar advantage. This took me a long time, because my code is spaghetti, and also because random in JavaScript is so much better than in C++ The visual changes I changed the theming of the game because I didn’t think the discord-esque style looked very good. Now, it’s changed to match the “Arch” theme on Monkeytype. I also changed the font to Roboto Mono since that’s what my configuration of Monkeytype uses. Stuff to do Compile this for Windows and Mac, put this on Itch.io. Ship the project"}, {"post_id": 70648, "ts": "2026-09-19T14:52:27Z", "hours": 0.4536, "text": "Devlog #35 (b48efd0): I fixed most of the font scaling issues, and also updated the README at bit (as always). I did this by loading the same font 3 times, once at 20 font size, once at 40, and once at 80. I changed the code to use a either the 20, 40, or 80 version of the font according to the font size to make the text look smoother."}, {"post_id": 69944, "ts": "2026-09-18T03:21:41Z", "hours": 2.6569, "text": "Devlog #34 (4eec49b): I changed the font! And also updated the README. Fonts in raylib are REALLY hard. Since I’m using multiple different font sizes, the scaling is really screwed up and blurry sometimes. When I was trying to add the custom font, I may or may not have… lagged out my computer and had to restart. as for the readme upgrade, i added instructions to play i also removed some semicolons. took me like half and hour"}, {"post_id": 67849, "ts": "2026-09-14T03:57:37Z", "hours": 1.2122, "text": "Devlog #33 (d16d3b8): This time was used for trying to make the project work for webassembly, which i have decided to abandon. Therefore, this time should probably be deflated to account for the lack of progress. I probably picked the worst language for compiling for the web, and next time I want to make a web version of anything, I’m going to use a game engine. This just did not work out, and was too much effort to save maybe ~2 seconds of time. I’m sorry that this will cause any reviewers to have to download the game to play it, and to compensate I will be attaching a link to a YouTube video demoing the game either on the readme, on the itch.io page, or both. I feel like a failure for this, but maybe it’s the right choice for my mental health and academic… idk is health also the right word for this situation?"}, {"post_id": 67685, "ts": "2026-09-13T21:11:46Z", "hours": 2.8528, "text": "Devlog #32 (ca5f14e): I changed the theming yet again. I’m not really good at making programs look good, and even when I do, my designs are usually really basic. Yes, it’s inspired by discord. Please tell me how this looks in the comments, I really need feedback. I also finally added a little change in color to some of the buttons when selected, which was the entire reason I decided to rework buttons in the first place. I’ll also attach a beta design that’s more of a retro/terminal aesthetic, but contrasted too much with the cube and letters IMO"}, {"post_id": 66298, "ts": "2026-09-12T01:53:47Z", "hours": 1.1728, "text": "Devlog #31 (6639676): I fixed the code! Essentially, the code was breaking because the hover info for the code was undefined. I fixed it by setting the hover info to the regular button info if the hover info was not defined. Then I ran into another issue, which was that the buttons, upon hover, were being drawn from the center instead of the corner. It was a simple enough fix, I just changed like 2 values to use the calibrated values instead of the center coordinates. And yeah, the only real issue right now is that the text on the board is slightly offcentre, but that’s not really a big issue."}, {"post_id": 65739, "ts": "2026-09-11T02:41:43Z", "hours": 7.9714, "text": "Devlog #30 (b5f8085): The code is now compiling again, thanks to, as much as I hate to admit it, Visual Studio Code. I’m on Linux so I can’t use Visual Studio, CLion is kinda weird and the error messages are broken, and KDevelop doesn’t have a Wakatime/Hackatime plugin yet (maybe it does, but it isn’t in the Hackatime docs) I opened VSCode and was met with like a hundred errors. With a combination of VSCode’s functional error messages, DeepSeek telling me what those messages mean, and my pretty average brain, I managed to remove all the errors. But since I didn’t know how to use makefiles and stuff (still don’t ) I had to compile the program manually again. Apart from the fact that it crashes upon any user mouse input, it’s functional!"}, {"post_id": 63870, "ts": "2026-09-07T21:22:41Z", "hours": 0.4178, "text": "Devlog #29 (57bac36): Guess what I did: a: Made my code work again b: Add new features c: Complete the button rice d: Look up the GitHub Repo for DeepSeek because I got bored and discover how to add badges to my README. I had way to much fun doing this (it was d)"}, {"post_id": 63786, "ts": "2026-09-07T19:01:29Z", "hours": 9.4011, "text": "Devlog #28 (ac77900): I added an entire separate .cpp file for handling buttons because I wasn’t satisfied with how they were working (I love ricing). Yes, it did take 9 hours. I have a skill issue. There are 3 main things I improved on: The buttons are now more customizable, as they now accept JSON files as setup input instead of like only 4 parameters. A function was added to change the button upon hover It’s more organized since the files are separate. (I haven’t actually tested the code out yet, I feel pressured to get the devlog out ASAP)"}, {"post_id": 60009, "ts": "2026-09-02T03:12:55Z", "hours": 1.3197, "text": "Devlog #27 (a1b005f): I highkey have trouble keeping track of what I did between devlogs sometimes, so I’m adding the SHA of the Git commit to devlogs. So I tried to create a dedicated c++ file for handling buttons, since I wanted to have even more control over them, but ran into the issue of not having a way to share variables across different files. I know global variables are kinda bad, but I mean in this scenario they’re like config stuff that isn’t really going to be that harmful… Most of the time was spent creating the button.cpp and button.h files, and they’re not even in GitHub yet lol"}, {"post_id": 59291, "ts": "2026-09-01T00:20:03Z", "hours": 0.7611, "text": "Devlog #26: Pretty short devlog. I added some simple QoL changes: the main menu title is now centered, and the popup and some captions now have a margin around them so they aren’t stuck right on the edge. Also this was technically done a couple of devlogs ago, but I added shell scripts to compile and run the program so I don’t have to type g++ -std=c++17 -Wall -Wextra -Werror -pedantic -o build/tic-tac-cube src/main.cpp src/board.cpp src/bot.cpp src/logs.cpp -lraylib every single time I want to compile the program."}, {"post_id": 58538, "ts": "2026-08-30T23:41:22Z", "hours": 7.5928, "text": "Devlog #25: I tried to make the UI look good. It isn’t necessarily good, but it’s better than it used to be IMO. I just changed the color scheme, and now the text is round and the boxes are rounded. I also reworked the board position display system. Since the squares are just buttons, and the button class can display text, I just made the board display text by itself. For context I wrote the button class to draw and handle buttons. There might be a class for this or something but idc i just wrote it myself like 17 days ago."}, {"post_id": 57061, "ts": "2026-08-29T04:32:35Z", "hours": 7.4608, "text": "(How is it) Devlog #24 (Already): Lowkey, when I started this project I was expecting to take 1 week max, and it was supposed to be like practice for making a more advanced project. I’ve since decided to not make the more advanced project (for now). Now, it’s been more than 2 weeks since I’ve started it, and it’s still in rough shape and probably not ready for release. The UI still sucks, but I added like a popup system for displaying messages, such as telling the user why they won/lost without the win being visible due to the nature of the board being a cube. I was going to make a class to handle all the popups, but I settled for global variables and functions. Probably not the best practice, but it works, and if it ain’t broke don’t fix it. I also added a entire new menu, the help menu. It could probably use some improvements, so if you have anything you’re curious about or confused about, drop a comment. There’s like 8 people that have seen this project, so I’m really expecting anything, but it would be appreciated greatly. I lowkey forgot to add one break statement, so I spent like 30 minutes trying to figure out why it wasn’t working Still, how the hell did this take 7 hours? I swear I’m not inflating my time by any means."}, {"post_id": 56171, "ts": "2026-08-27T20:08:34Z", "hours": 2.4689, "text": "Devlog #23: “Minor” improvements that take like hours to do is literally my entire C++ learning experience so far. This time, I improved the “wild card” function that rotates the cube randomly. Before, it just actually rotated randomly, and now, I changed it to check and make sure that moves it makes does not cause the opponent to win on the very next move. fyi i DO write these devlogs myself without ai, I just literally need Vim keybinds to function as a person and therefore type all the devlogs inside of NeoVim before pasting them into the devlog. felt like writing this after i discovered that the “please write this yourself, humans read it” text at the bottom actually flashes red when you paste text."}, {"post_id": 55363, "ts": "2026-08-27T04:29:07Z", "hours": 3.4486, "text": "Devlog #22: I made the buttons look nicer by centering the text inside of them. It was much harder than it looked and I had to work with “Vector2” in Raylib. I may or may not have spent like half and hour trying to find documentation on it, only for me to discover that it’s a struct inside of Raylib. Still not entirely sure what a struct is. So I may or may not have spent another half and hour trying to declare the struct inside of the main.cpp file, before I finally gave up and asked DeepSeek. Turns out, it’s just Vector2 something{(float)x, (float)y} . It’s not exactly simple if you don’t have much experience with C++, but I started coding in C++ 16 days ago and I understand it just fine. Halfway through, while testing, I accidentally overrode the main file and had to restore it and re-add all the changes I make. I don’t like committing before the program doesn’t have that many bugs, so a lot of progress was lost. This is like the second time I’ve done this, and if you’re curious what I did, well… The command used to compile my program for testing is g++ -std=c++17 -o build/program src/main.cpp src/board.cpp src/bot.cpp src/logs.cpp -lrayib . This compiles the code for all the C++ files and the Raylib library, and creates and executable inside the build/ directory. Except I forgot to type build/program, and so GCC just overrode main.cpp. Honestly though, it didn’t do that much harm, thanks to the test.cpp file having a similar version of code as the code that was overridden, and also git. hackatime isn’t working so i can’t even post this devlog nvm it just worked nvm it broke again honestly this is one of the first times anything has broken in my experience as someone who joined hack club like 3 months ago. im sure everyone involved in fixing this is trying their absolute best."}, {"post_id": 55210, "ts": "2026-08-26T19:20:18Z", "hours": 2.4914, "text": "Devlog #21: I am very very very very bad at debugging code. Somehow I managed to fix the major rendering bug and also make the fork handling better within 2 hours. I don’t believe I’ve mentioned anything about it before, but there was a rendering glitch upon the bot winning the game right after the user has made a cube turn that allowed the bot to win the game. I’m still not sure wy that happened, but I fixed it by re-rendering the entire board before drawing the board position again. If that didn’t make sense to you, it doesn’t make much sense to me either. If it did, well good for you ig. I feel like the fork handling deserves and entire paragraph, so here: I changed the win blocking function to also log the direction that potential win is in, by logging “position 1” of that specific potential win. The possible neutralizing moves of the directional indicator value is then compared against the potential win location neutralizing move to try to find a match. If a match is found, that means that the move is guaranteed to neutralize the given threat. That paragraph did not make much sense, even to me. If you read my code, it’ll probably make more sense, as long as you do not get permanent brain damage from reading the it (I need to learn how to write better code)"}, {"post_id": 54902, "ts": "2026-08-26T03:54:05Z", "hours": 9.4122, "text": "Devlog #20: I abandoned searching for potential forks and stopping them from ever forming in favour of handling forks that have already been formed. After more than one win for the opponent is detected, the bot registers that it is a fork. It then calculates which moves are best to neutralize the threat. There are four ways for each single win to be neutralized, so I need to make one of these four moves, for each of the two cases. In some cases, one or more of the moves overlap, in which case the overlapped move will be immediately returned, but most of the time, the bot just picks from one of the 8 possible neutralization moves randomly. This is definitely less efficient than just figuring out how to prevent forks from ever forming, but I mean it’s a good implementation for the cube part of this game. (Sometimes it messes up and I just win instantly)"}, {"post_id": 54048, "ts": "2026-08-24T20:12:14Z", "hours": 5.1683, "text": "Devlog #19: In an effort to procrastinate fixing the bot fork bug while still doing something useful, I 1) took a 2 day break from this project to work (and ship) something else, and 2) fixed a very annoying bug where the user would be X every single time. I solved the issue by switching from older, more C-like rand() from cstdlib to the more modern #include <random> . I highkey copied the code from Stack Overflow , but at least it’s better than relying on AI. That solved like half of the problem, but then I discovered that I wasn’t actually resetting the board properly when manually exiting a game, since I was looking through the logs. I made the logs are procrastination, but they’re actually really nice. In case you were wondering btw, most of the time logged in this devlog was spent on trying to fix the fork bug, but I’ll make a dedicated devlog only when I’ve actually fixed the bug."}, {"post_id": 52065, "ts": "2026-08-22T03:58:57Z", "hours": 3.9525, "text": "Devlog #18: I worked on the logs even more, and now they are in separate files. You wouldn’t believe the amount of time this took me. Evidently, I’m not a very good C++ dev, and when I was searching on Stack Overflow and other sites, I kept running into things I don’t understand like & , auto , and probably many others. So of course, I resorted to coding my own functions at the most low level C++ possible without actually being totally backwards compatible with C. Essentially, every time the game starts, function runs that tries to generate a log file with the current time. There is a theoretically possible edge case where the user opens the project more than once in a single second, so I added some code to make absolute sure not to override any existing files. That took me like at least 15 minutes to half and hour, for an edge case that probably can only happen in testing, and that will not actually affect the game at all. Yay, what great decision making skills. I still haven’t fixed the fork detection and response function, but at least I can now have a way to find out what is going wrong. The only progress with finding out what is wrong is just narrowing the error location down to the function where I already knew there was going to be an error. Yay."}, {"post_id": 51891, "ts": "2026-08-21T19:50:23Z", "hours": 2.0631, "text": "Devlog #17: Guess what I did today: a) fixed the fork, b) optimized UI, c) hunted bugs, or d) added a random new c++ file for the sole purpose of logging information because I don’t want to use an IDE or configure NeoVim. The answer, of course, is D. I really need a way to figure out what is happening inside my code. Even though I wrote the code, it’s still kinda a black box to me, so I need to have some logs to figure out why the program is not working as expected. I have yet to actually implement the function or add out a way to have different logs files for different sessions though, I’m doing that next."}, {"post_id": 51427, "ts": "2026-08-21T03:58:41Z", "hours": 8.3636, "text": "Devlog #16: I added creating and defending against forks. One of my previous projects, Gomoku Balanced, had some decent defense and offense, which was even able to beat some people, according to the reviews on my ship. The one thing it was missing was the ability to detect potential forks and combat them, which was why I was able to beat it almost every single time. This bot, however, will not be the same. Mostly because the board is kinda small. I literally just added every single possibility of a fork to the to the bot. I just got notified that I have like 8 hours without a devlog, and since I am horrible at coding, the bot function I’m working on isn’t even working yet. I’m also just not getting it done within 2 hours. The function is supposed to block the fork, but it doesn’t work for some reason. DeepSeek (AI) gave me a list of 20 reasons it’s breaking, but I’m not about to blindly trust AI. I mean it doesn’t raise any errors, so that’s good. For me at least, I spend a lot of time just trying to get the code to compile properly. And in this case, even when the code compiles, half the time it just runs into a runtime error and crashes. The bot crashed like 20 times. They were all runtime errors, and I use C++, so I have absolutely no idea where the error occurs. Also, I want to be different, and therefore I use NeoVim, and since I don’t want to spend time that I could be building actually good projects for Stardance in the time it takes to configure NeoVim, and therefore I can’t see any warnings or errors. I did try using CLion and VSCode, but CLion takes up a lot of RAM and VSCode is made by Microsoft (and VSCodium and Code OSS don’t have that many plugins). Also, when I was using CLion, the errors didn’t really appear… and CLion also does a bunch of stuff and creates a bunch of mysterious files. I don’t really like doing stuff without knowing what they do, so I just ditched CLion altogether. Did I seriously go on a paragraph-long rant about how using NeoVim leads to not being able to know why my code sucks? Is the paragraph-long rant in question also probably longer than the entire rest of the devlog? (Yes and yes); Anyway you still can’t tell that I made any changes to the bot from a single screenshot, so I’m just going to attach one of my old ones."}, {"post_id": 50705, "ts": "2026-08-20T01:11:16Z", "hours": 2.1453, "text": "Devlog #15: I updated the bot to give it two of the most important features: winning the game, and trying to prevent your opponent from winning. This was done by saving every single possible way to have a two-in-a-row on the board, connected and disconnected, 24 possible combinations in total. And of course, because I’m not experienced enough to do it any other way, it was all written by hand. And since I don’t like using AI, and don’t really trust it, I wrote all of it myself, and used only used AI to check that I didn’t mess something up. Now how the hell do I make it detect forks…."}, {"post_id": 50618, "ts": "2026-08-19T20:57:50Z", "hours": 6.43, "text": "Devlog #14: In search of a better way to make a bot, I went on the world’s most reliable website: Wikipedia - and struck gold. The page on tic-tac-toe contains a strategy section, containing the optimal strategy that always leads to a draw. Of course, my game isn’t actually the same as Vanilla tic-tac-toe, so the player will probably still win most of the time. I haven’t actually implemented most of the rules, only the last few that are the least important. In my defense, they were the easiest to make. I did kinda struggle with the bot trying to mark a place that already has been placed, and it took like half and hour for me to figure that out. IDK what I should do for the screenshot, you can’t really tell that I changed anything."}, {"post_id": 49910, "ts": "2026-08-18T21:29:42Z", "hours": 6.7939, "text": "Devlog 13: I spent like 6 hours making the bot, and it’s absolutely stupid. I made two ways to get a move: scanning for 2-in-a-rows, and placing randomly. Even though the 2-in-a-row scan runs first, it still only plays randomly. As of right now, the bot has never won a single game against me and is always O for some reason. I probably messed up the setup code. but at least i made something that are somewhat functional….. (I was writing this devlog and trying to get a screenshot of the game, and somehow it actually worked and blocked my move somehow, IDK if it was a coincidence)"}, {"post_id": 49267, "ts": "2026-08-17T23:33:01Z", "hours": 2.055, "text": "Devlog #12: So I was going to implement the singleplayer mode, but turns out there was still a bunch of bugs and inconveniences that took me upwards of an hour to fix. Also, I lowkey put a break statement somewhere I shouldn’t have, and got the programming equivalent of a short-circuit. In the end, it did work fine, and now I actually will build the bot and singleplayer mode."}, {"post_id": 49122, "ts": "2026-08-17T19:10:00Z", "hours": 7.9347, "text": "Devlog #11: I was going to make a singleplayer mode for the game, but somewhere along the way I kinda messed my code up a lot, and had to undo nearly 6 hours of changes since the last devlog. I then spent like an hour fixing bugs and trying to organize code, mostly by moving code that could/should be reused. I’m going to start writing more functional code for singleplayer now :) Also I switched from Vim to NeoVim in case you care, and since not a lot of changes were actually implemented, Imma just attach a screenshot of a file opened in NVim"}, {"post_id": 48239, "ts": "2026-08-16T15:21:39Z", "hours": 1.3433, "text": "Devlog #10: I added an endscreen to the game. Before, when you won the game or lost the game, it would just go back to the main menu. Now, it displays a message according to which side won the game, and pauses for exactly one seconds before exiting back to the main menu. It probably still can be confusing since there’s no indication of where in the board the sequence that won the game is, and there is still a possibility that a cube rotation wins the game off the board, and I have no idea how to make more clear to the user what happened to win the game."}, {"post_id": 47846, "ts": "2026-08-16T02:15:52Z", "hours": 2.4331, "text": "Devlog #9: I added colors to the game. Colors are a crucial part in Rubik’s cubes, so it just makes sense to add it to this game, yknow. I basically just copied the code that rotates the board positions that I had to spend like 6 hours coding , and made it work with colors. Not actually that hard, but still almost made me go insane with how much mindless typing I had to do. It’s funny that when I am stuggling with things like using my brain and fixing broken code , I would like nothing more than some mindless coding, but now that I actually am doing this stuff, I am just wanting to do anything but. UI still looks ugly as hell, need to improve that later."}, {"post_id": 47741, "ts": "2026-08-15T21:13:06Z", "hours": 2.4297, "text": "Devlog 8: You can finally actually interact with the GUI. You can click the red squares to place an X or an O, or click on one of the white squares to rotate the cube. It’s kinda bad right now, and if you click one of the cube rotation things longer than 33 milliseconds, it will move several times. I mean, it’s good enough, I guess, apart from the fact that it looks… (I don’t know enough english to come up with a word that’s good enough, and my english grade in school is 95%)"}, {"post_id": 47618, "ts": "2026-08-15T16:56:17Z", "hours": 6.0747, "text": "Devlog #7: I got the GUI working for the game. I mean, technically, to be a GUI you have to be able to interact with it, and in it’s current state, uh you can’t really do anything and you’re just stuck there forever. I mean unless you decide to close the game window or press escape (which closes the game window). Ideally, I would like for the escape key to not close the window, but whatever at this stage it really does not matter. List of stuff I need to do: Make it so that the user can actually interact with the game. Make a bot or something Make the UI nicer Put in on Itch.io this actually looks kinda intimidating. what have i gotten myself in to"}, {"post_id": 46675, "ts": "2026-08-14T03:38:13Z", "hours": 4.9258, "text": "Devlog #6: I got a working GUI for the main menu! This is my first GUI that is actually like usable, but since the actual gameplay doesn’t have a GUI yet, it does nothing except closing when the exit button is pressed. I made the UI with Raylib, and apart from the occasional existential crisis, everything went pretty well. now how the hell do i make the actual game ui"}, {"post_id": 46051, "ts": "2026-08-12T23:59:49Z", "hours": 2.4667, "text": "Devlog #5: I added a main menu to the game. It’s still only Text UI, so it may not make sense why it took so long, but I moved all the functions inside a class, make a header file so that the class can be accessed in other C++ files, discovered that it doesn’t work, had an hour long conversation with deepseek about why this didn’t work, and finally got it working- just to implement one feature that would’ve taken like 5 minutes had I just made everything in one file. At least it’s going to be organized for when I add the GUI or other features."}, {"post_id": 45916, "ts": "2026-08-12T18:06:02Z", "hours": 3.4297, "text": "Devlog #4: You can now actually win the game! I wrote a function that scans all the possible ways to win on every face, with amounts to 6x8=48 possible combinations. So there’s a chance that you rotate a face of the cube, and win or lose instantly due to the face you rotated to already having like 2 in a row. Now, I just need to make the GUI, not sure how I’m even going to implement it. I know 2 of the options are Raylib and SDL2, but I honestly have no idea which one to pick."}, {"post_id": 44963, "ts": "2026-08-11T02:19:30Z", "hours": 3.5108, "text": "Devlog #3: I made the cube turning mechanic. Although I have made programs like this before (like my CubeTrainer ), I struggled a lot with making the code, especially the Z function, and the two functions that depend on it, L and R. This was entirely human error and typos though, and not really like intellectually challenging. So I added the ability to turn the cube finally. Most of the main features of the game are done now, I just need a GUI, main menu, bot, and… lowkey ran out of ideas so yeah. To be very honest, my experiences with GUIs in C++ is limited to drawing a single dot in sdl2, and that took me 3 hours to do. And I lowkey cannot find any tutorials on SDL2 that can satisfy my attention span. I am very much someone that learns by doing, and most SDL2 tutorials I found just tell me to copy and paste code word for word."}, {"post_id": 44443, "ts": "2026-08-10T06:12:06Z", "hours": 3.8533, "text": "Devlog #2: This is like my 3rd day of serious C++ coding, and I’ve already build a functional version of tic-tac-toe. That wasn’t really supposed to be impressive, but I am kinda surprised I managed to pull this off so quickly (by my standards, for context it took me like 3 year to make an actually decent Python project). So yeah, I made tic-tac-toe. So far it only works in the terminal as I have no idea how to make a functional GUI in C++. There are also safeguards in place to prevent placing where it’s already marked or out of bounds. I still need to implement rotating the cube, but that can’t be that hard…."}, {"post_id": 44282, "ts": "2026-08-09T22:49:28Z", "hours": 2.3694, "text": "Devlog #1: I suck at C++, so I decided to make tic-tac-toe on the different faces on a Rubik’s cube. Right now the program just prints out the default position (all blank), and you can’t change it or anything. Yes, this took me two hours. There were a lot of bugs and I have almost never used C++ before (I had to google how to output text like twice)"}]...
|
Title: Tic-Tac-Cube by @EthanCubes | Stardance
De Title: Tic-Tac-Cube by @EthanCubes | Stardance
Devlogs:
Devlog #1: I suck at C++, so I decided to make tic-tac-toe on the different faces on a Rubik’s cube. Right now the program just prints out the default position (all blank), and you can’t change it or anything. Yes, this took me two hours. There were a lot of bugs and I have almost never used C++ before (I had to google how to output text like twice)
Devlog #2: This is like my 3rd day of serious C++ coding, and I’ve already build a functional version of tic-tac-toe. That wasn’t really supposed to be impressive, but I am kinda surprised I managed to pull this off so quickly (by my standards, for context it took me like 3 year to make an actually decent Python project). So yeah, I made tic-tac-toe. So far it only works in the terminal as I have no idea how to make a functional GUI in C++. There are also safeguards in place to prevent placing where it’s already marked or out of bounds. I still need to implement rotating the cube, but that can’t be that hard….
Devlog #3: I made the cube turning mechanic. Although I have made programs like this before (like my CubeTrainer ), I struggled a lot with making the code, especially the Z function, and the two functions that depend on it, L and R. This was entirely human error and typos though, and not really like intellectually challenging. So I added the ability to turn the cube finally. Most of the main features of the game are done now, I just need a GUI, main menu, bot, and… lowkey ran out of ideas so yeah. To be very honest, my experiences with GUIs in C++ is limited to drawing a single dot in sdl2, and that took me 3 hours to do. And I lowkey cannot find any tutorials on SDL2 that can satisfy my attention span. I am very much someone that learns by doing, and most SDL2 tutorials I found just tell me to copy and paste code word for word.
Devlog #4: You can now actually win the game! I wrote a function that scans all the possible ways to win on every face, with amounts to 6x8=48 possible combinations. So there’s a chance that you rotate a face of the cube, and win or lose instantly due to the face you rotated to already having like 2 in a row. Now, I just need to make the GUI, not sure how I’m even going to implement it. I know 2 of the options are Raylib and SDL2, but I honestly have no idea which one to pick.
Devlog #5: I added a main menu to the game. It’s still only Text UI, so it may not make sense why it took so long, but I moved all the functions inside a class, make a header file so that the class can be accessed in other C++ files, discovered that it doesn’t work, had an hour long conversation with deepseek about why this didn’t work, and finally got it working- just to implement one feature that would’ve taken like 5 minutes had I just made everything in one file. At least it’s going to be organized for when I add the GUI or other features.
Devlog #6: I got a working GUI for the main menu! This is my first GUI that is actually like usable, but since the actual gameplay doesn’t have a GUI yet, it does nothing except closing when the exit button is pressed. I made the UI with Raylib, and apart from the occasional existential crisis, everything went pretty well. now how the hell do i make the actual game ui
Devlog #7: I got the GUI working for the game. I mean, technically, to be a GUI you have to be able to interact with it, and in it’s current state, uh you can’t really do anything and you’re just stuck there forever. I mean unless you decide to close the game window or press escape (which closes the game window). Ideally, I would like for the escape key to not close the window, but whatever at this stage it really does not matter. List of stuff I need to do: Make it so that the user can actually interact with the game. Make a bot or something Make the UI nicer Put in on Itch.io this actually looks kinda intimidating. what have i gotten myself in to
Devlog 8: You can finally actually interact with the GUI. You can click the red squares to place an X or an O, or click on one of the white squares to rotate the cube. It’s kinda bad right now, and if you click one of the cube rotation things longer than 33 milliseconds, it will move several times. I mean, it’s good enough, I guess, apart from the fact that it looks… (I don’t know enough english to come up with a word that’s good enough, and my english grade in school is 95%)
Devlog #9: I added colors to the game. Colors are a crucial part in Rubik’s cubes, so it just makes sense to add it to this game, yknow. I basically just copied the code that rotates the board positions that I had to spend like 6 hours coding , and made it work with colors. Not actually that hard, but still almost made me go insane with how much mindless typing I had to do. It’s funny that when I am stuggling with things like using my brain and fixing broken code , I would like nothing more than some mindless coding, but now that I actually am doing this stuff, I am just wanting to do anything but. UI still looks ugly as hell, need to improve that later.
Devlog #10: I added an endscreen to the game. Before, when you won the game or lost the game, it would just go back to the main menu. Now, it displays a message according to which side won the game, and pauses for exactly one seconds before exiting back to the main menu. It probably still can be confusing since there’s no indication of where in the board the sequence that won the game is, and there is still a possibility that a cube rotation wins the game off the board, and I have no idea how to make more clear to the user what happened to win the game.
Devlog #11: I was going to make a singleplayer mode for the game, but somewhere along the way I kinda messed my code up a lot, and had to undo nearly 6 hours of changes since the last devlog. I then spent like an hour fixing bugs and trying to organize code, mostly by moving code that could/should be reused. I’m going to start writing more functional code for singleplayer now :) Also I switched from Vim to NeoVim in case you care, and since not a lot of changes were actually implemented, Imma just attach a screenshot of a file opened in NVim
Devlog #12: So I was going to implement the singleplayer mode, but turns out there was still a bunch of bugs and inconveniences that took me upwards of an hour to fix. Also, I lowkey put a break statement somewhere I shouldn’t have, and got the programming equivalent of a short-circuit. In the end, it did work fine, and now I actually will build the bot and singleplayer mode.
Devlog 13: I spent like 6 hours making the bot, and it’s absolutely stupid. I made two ways to get a move: scanning for 2-in-a-rows, and placing randomly. Even though the 2-in-a-row scan runs first, it still only plays randomly. As of right now, the bot has never won a single game against me and is always O for some reason. I probably messed up the setup code. but at least i made something that are somewhat functional….. (I was writing this devlog and trying to get a screenshot of the game, and somehow it actually worked and blocked my move somehow, IDK if it was a coincidence)
Devlog #14: In search of a better way to make a bot, I went on the world’s most reliable website: Wikipedia - and struck gold. The page on tic-tac-toe contains a strategy section, containing the optimal strategy that always leads to a draw. Of course, my game isn’t actually the same as Vanilla tic-tac-toe, so the player will probably still win most of the time. I haven’t actually implemented most of the rules, only the last few that are the least important. In my defense, they were the easiest to make. I did kinda struggle with the bot trying to mark a place that already has been placed, and it took like half and hour for me to figure that out. IDK what I should do for the screenshot, you can’t really tell that I changed anything.
Devlog #15: I updated the bot to give it two of the most important features: winning the game, and trying to prevent your opponent from winning. This was done by saving every single possible way to have a two-in-a-row on the board, connected and disconnected, 24 possible combinations in total. And of course, because I’m not experienced enough to do it any other way, it was all written by hand. And since I don’t like using AI, and don’t really trust it, I wrote all of it myself, and used only used AI to check that I didn’t mess something up. Now how the hell do I make it detect forks….
Devlog #16: I added creating and defending against forks. One of my previous projects, Gomoku Balanced, had some decent defense and offense, which was even able to beat some people, according to the reviews on my ship. The one thing it was missing was the ability to detect potential forks and combat them, which was why I was able to beat it almost every single time. This bot, however, will not be the same. Mostly because the board is kinda small. I literally just added every single possibility of a fork to the to the bot. I just got notified that I have like 8 hours without a devlog, and since I am horrible at coding, the bot function I’m working on isn’t even working yet. I’m also just not getting it done within 2 hours. The function is supposed to block the fork, but it doesn’t work for some reason. DeepSeek (AI) gave me a list of 20 reasons it’s breaking, but I’m not about to blindly trust AI. I mean it doesn’t raise any errors, so that’s good. For me at least, I spend a lot of time just trying to get the code to compile properly. And in this case, even when the code compiles, half the time it just runs into a runtime error and crashes. The bot crashed like 20 times. They were all runtime errors, and I use C++, so I have absolutely no idea where the error occurs. Also, I want to be different, and therefore I use NeoVim, and since I don’t want to spend time that I could be building actually good projects for Stardance in the time it takes to configure NeoVim, and therefore I can’t see any warnings or errors. I did try using CLion and VSCode, but CLion takes up a lot of RAM and VSCode is made by Microsoft (and VSCodium and Code OSS don’t have that many plugins). Also, when I was using CLion, the errors didn’t really appear… and CLion also does a bunch of stuff and creates a bunch of mysterious files. I don’t really like doing stuff without knowing what they do, so I just ditched CLion altogether. Did I seriously go on a paragraph-long rant about how using NeoVim leads to not being able to know why my code sucks? Is the paragraph-long rant in question also probably longer than the entire rest of the devlog? (Yes and yes); Anyway you still can’t tell that I made any changes to the bot from a single screenshot, so I’m just going to attach one of my old ones.
Devlog #17: Guess what I did today: a) fixed the fork, b) optimized UI, c) hunted bugs, or d) added a random new c++ file for the sole purpose of logging information because I don’t want to use an IDE or configure NeoVim. The answer, of course, is D. I really need a way to figure out what is happening inside my code. Even though I wrote the code, it’s still kinda a black box to me, so I need to have some logs to figure out why the program is not working as expected. I have yet to actually implement the function or add out a way to have different logs files for different sessions though, I’m doing that next.
Devlog #18: I worked on the logs even more, and now they are in separate files. You wouldn’t believe the amount of time this took me. Evidently, I’m not a very good C++ dev, and when I was searching on Stack Overflow and other sites, I kept running into things I don’t understand like & , auto , and probably many others. So of course, I resorted to coding my own functions at the most low level C++ possible without actually being totally backwards compatible with C. Essentially, every time the game starts, function runs that tries to generate a log file with the current time. There is a theoretically possible edge case where the user opens the project more than once in a single second, so I added some code to make absolute sure not to override any existing files. That took me like at least 15 minutes to half and hour, for an edge case that probably can only happen in testing, and that will not actually affect the game at all. Yay, what great decision making skills. I still haven’t fixed the fork detection and response function, but at least I can now have a way to find out what is going wrong. The only progress with finding out what is wrong is just narrowing the error location down to the function where I already knew there was going to be an error. Yay.
Devlog #19: In an effort to procrastinate fixing the bot fork bug while still doing something useful, I 1) took a 2 day break from this project to work (and ship) something else, and 2) fixed a very annoying bug where the user would be X every single time. I solved the issue by switching from older, more C-like rand() from cstdlib to the more modern #include <random> . I highkey copied the code from Stack Overflow , but at least it’s better than relying on AI. That solved like half of the problem, but then I discovered that I wasn’t actually resetting the board properly when manually exiting a game, since I was looking through the logs. I made the logs are procrastination, but they’re actually really nice. In case you were wondering btw, most of the time logged in this devlog was spent on trying to fix the fork bug, but I’ll make a dedicated devlog only when I’ve actually fixed the bug.
Devlog #20: I abandoned searching for potential forks and stopping them from ever forming in favour of handling forks that have already been formed. After more than one win for the opponent is detected, the bot registers that it is a fork. It then calculates which moves are best to neutralize the threat. There are four ways for each single win to be neutralized, so I need to make one of these four moves, for each of the two cases. In some cases, one or more of the moves overlap, in which case the overlapped move will be immediately returned, but most of the time, the bot just picks from one of the 8 possible neutralization moves randomly. This is definitely less efficient than just figuring out how to prevent forks from ever forming, but I mean it’s a good implementation for the cube part of this game. (Sometimes it messes up and I just win instantly)
Devlog #21: I am very very very very bad at debugging code. Somehow I managed to fix the major rendering bug and also make the fork handling better within 2 hours. I don’t believe I’ve mentioned anything about it before, but there was a rendering glitch upon the bot winning the game right after the user has made a cube turn that allowed the bot to win the game. I’m still not sure wy that happened, but I fixed it by re-rendering the entire board before drawing the board position again. If that didn’t make sense to you, it doesn’t make much sense to me either. If it did, well good for you ig. I feel like the fork handling deserves and entire paragraph, so here: I changed the win blocking function to also log the direction that potential win is in, by logging “position 1” of that specific potential win. The possible neutralizing moves of the directional indicator value is then compared against the potential win location neutralizing move to try to find a match. If a match is found, that means that the move is guaranteed to neutralize the given threat. That paragraph did not make much sense, even to me. If you read my code, it’ll probably make more sense, as long as you do not get permanent brain damage from reading the it (I need to learn how to write better code)
Devlog #22: I made the buttons look nicer by centering the text inside of them. It was much harder than it looked and I had to work with “Vector2” in Raylib. I may or may not have spent like half and hour trying to find documentation on it, only for me to discover that it’s a struct inside of Raylib. Still not entirely sure what a struct is. So I may or may not have spent another half and hour trying to declare the struct inside of the main.cpp file, before I finally gave up and asked DeepSeek. Turns out, it’s just Vector2 something{(float)x, (float)y} . It’s not exactly simple if you don’t have much experience with C++, but I started coding in C++ 16 days ago and I understand it just fine. Halfway through, while testing, I accidentally overrode the main file and had to restore it and re-add all the changes I make. I don’t like committing before the program doesn’t have that many bugs, so a lot of progress was lost. This is like the second time I’ve done this, and if you’re curious what I did, well… The command used to compile my program for testing is g++ -std=c++17 -o build/program src/main.cpp src/board.cpp src/bot.cpp src/logs.cpp -lrayib . This compiles the code for all the C++ files and the Raylib library, and creates and executable inside the build/ directory. Except I forgot to type build/program, and so GCC just overrode main.cpp. Honestly though, it didn’t do that much harm, thanks to the test.cpp file having a similar version of code as the code that was overridden, and also git. hackatime isn’t working so i can’t even post this devlog nvm it just worked nvm it broke again honestly this is one of the first times anything has broken in my experience as someone who joined hack club like 3 months ago. im sure everyone involved in fixing this is trying their absolute best.
Devlog #23: “Minor” improvements that take like hours to do is literally my entire C++ learning experience so far. This time, I improved the “wild card” function that rotates the cube randomly. Before, it just actually rotated randomly, and now, I changed it to check and make sure that moves it makes does not cause the opponent to win on the very next move. fyi i DO write these devlogs myself without ai, I just literally need Vim keybinds to function as a person and therefore type all the devlogs inside of NeoVim before pasting them into the devlog. felt like writing this after i discovered that the “please write this yourself, humans read it” text at the bottom actually flashes red when you paste text.
(How is it) Devlog #24 (Already): Lowkey, when I started this project I was expecting to take 1 week max, and it was supposed to be like practice for making a more advanced project. I’ve since decided to not make the more advanced project (for now). Now, it’s been more than 2 weeks since I’ve started it, and it’s still in rough shape and probably not ready for release. The UI still sucks, but I added like a popup system for displaying messages, such as telling the user why they won/lost without the win being visible due to the nature of the board being a cube. I was going to make a class to handle all the popups, but I settled for global variables and functions. Probably not the best practice, but it works, and if it ain’t broke don’t fix it. I also added a entire new menu, the help menu. It could probably use some improvements, so if you have anything you’re curious about or confused about, drop a comment. There’s like 8 people that have seen this project, so I’m really expecting anything, but it would be appreciated greatly. I lowkey forgot to add one break statement, so I spent like 30 minutes trying to figure out why it wasn’t working Still, how the hell did this take 7 hours? I swear I’m not inflating my time by any means.
Devlog #25: I tried to make the UI look good. It isn’t necessarily good, but it’s better than it used to be IMO. I just changed the color scheme, and now the text is round and the boxes are rounded. I also reworked the board position display system. Since the squares are just buttons, and the button class can display text, I just made the board display text by itself. For context I wrote the button class to draw and handle buttons. There might be a class for this or something but idc i just wrote it myself like 17 days ago.
Devlog #26: Pretty short devlog. I added some simple QoL changes: the main menu title is now centered, and the popup and some captions now have a margin around them so they aren’t stuck right on the edge. Also this was technically done a couple of devlogs ago, but I added shell scripts to compile and run the program so I don’t have to type g++ -std=c++17 -Wall -Wextra -Werror -pedantic -o build/tic-tac-cube src/main.cpp src/board.cpp src/bot.cpp src/logs.cpp -lraylib every single time I want to compile the program.
Devlog #27 (a1b005f): I highkey have trouble keeping track of what I did between devlogs sometimes, so I’m adding the SHA of the Git commit to devlogs. So I tried to create a dedicated c++ file for handling buttons, since I wanted to have even more control over them, but ran into the issue of not having a way to share variables across different files. I know global variables are kinda bad, but I mean in this scenario they’re like config stuff that isn’t really going to be that harmful… Most of the time was spent creating the button.cpp and button.h files, and they’re not even in GitHub yet lol
Devlog #28 (ac77900): I added an entire separate .cpp file for handling buttons because I wasn’t satisfied with how they were working (I love ricing). Yes, it did take 9 hours. I have a skill issue. There are 3 main things I improved on: The buttons are now more customizable, as they now accept JSON files as setup input instead of like only 4 parameters. A function was added to change the button upon hover It’s more organized since the files are separate. (I haven’t actually tested the code out yet, I feel pressured to get the devlog out ASAP)
Devlog #29 (57bac36): Guess what I did: a: Made my code work again b: Add new features c: Complete the button rice d: Look up the GitHub Repo for DeepSeek because I got bored and discover how to add badges to my README. I had way to much fun doing this (it was d)
Devlog #30 (b5f8085): The code is now compiling again, thanks to, as much as I hate to admit it, Visual Studio Code. I’m on Linux so I can’t use Visual Studio, CLion is kinda weird and the error messages are broken, and KDevelop doesn’t have a Wakatime/Hackatime plugin yet (maybe it does, but it isn’t in the Hackatime docs) I opened VSCode and was met with like a hundred errors. With a combination of VSCode’s functional error messages, DeepSeek telling me what those messages mean, and my pretty average brain, I managed to remove all the errors. But since I didn’t know how to use makefiles and stuff (still don’t ) I had to compile the program manually again. Apart from the fact that it crashes upon any user mouse input, it’s functional!
Devlog #31 (6639676): I fixed the code! Essentially, the code was breaking because the hover info for the code was undefined. I fixed it by setting the hover info to the regular button info if the hover info was not defined. Then I ran into another issue, which was that the buttons, upon hover, were being drawn from the center instead of the corner. It was a simple enough fix, I just changed like 2 values to use the calibrated values instead of the center coordinates. And yeah, the only real issue right now is that the text on the board is slightly offcentre, but that’s not really a big issue.
Devlog #32 (ca5f14e): I changed the theming yet again. I’m not really good at making programs look good, and even when I do, my designs are usually really basic. Yes, it’s inspired by discord. Please tell me how this looks in the comments, I really need feedback. I also finally added a little change in color to some of the buttons when selected, which was the entire reason I decided to rework buttons in the first place. I’ll also attach a beta design that’s more of a retro/terminal aesthetic, but contrasted too much with the cube and letters IMO
Devlog #33 (d16d3b8): This time was used for trying to make the project work for webassembly, which i have decided to abandon. Therefore, this time should probably be deflated to account for the lack of progress. I probably picked the worst language for compiling for the web, and next time I want to make a web version of anything, I’m going to use a game engine. This just did not work out, and was too much effort to save maybe ~2 seconds of time. I’m sorry that this will cause any reviewers to have to download the game to play it, and to compensate I will be attaching a link to a YouTube video demoing the game either on the readme, on the itch.io page, or both. I feel like a failure for this, but maybe it’s the right choice for my mental health and academic… idk is health also the right word for this situation?
Devlog #34 (4eec49b): I changed the font! And also updated the README. Fonts in raylib are REALLY hard. Since I’m using multiple different font sizes, the scaling is really screwed up and blurry sometimes. When I was trying to add the custom font, I may or may not have… lagged out my computer and had to restart. as for the readme upgrade, i added instructions to play i also removed some semicolons. took me like half and hour
Devlog #35 (b48efd0): I fixed most of the font scaling issues, and also updated the README at bit (as always). I did this by loading the same font 3 times, once at 20 font size, once at 40, and once at 80. I changed the code to use a either the 20, 40, or 80 version of the font according to the font size to make the text look smoother.
Devlog #36 (fcc4f2f): I made the first gameplay change in a while, and also changed the theming a bit more. The gameplay changes I noticed that the game was getting a bit… repetitive, so I changed the bot to have an element of randomness when there are multiple spots that provide similar advantage. This took me a long time, because my code is spaghetti, and also because random in JavaScript is so much better than in C++ The visual changes I changed the theming of the game because I didn’t think the discord-esque style looked very good. Now, it’s changed to match the “Arch” theme on Monkeytype. I also changed the font to Roboto Mono since that’s what my configuration of Monkeytype uses. Stuff to do Compile this for Windows and Mac, put this on Itch.io. Ship the project
Devlog #37 (8b55316): I made a web build for the game! I took a break from Stardance to make something for Out to C, and when I was making a C game with Raylib I discovered that it is extremely hard to make a downloadable executable run well on Windows or MacOS, because on Windows you have to include DLL files, and on MacOS it’s just harder to install Raylib. In the end, I ended up making a web build, and since I wasn’t so bad, I decided to make a web build for this. I used Emscripten, and using the em++ tool with the asyncify option, manage to get most of the game logic working, with only one small frame generation bug, which I fixed by changing this_thread::sleep for to something that is easier for the browser to render. I also made some other QoL changes so that the bot feels less stagnant, hopefully.
Devlog #38 (25c796f): the beans aren’t going to know what hit them (regarding my 145 hours waiting to be shipped) I made a couple of changes, such as improving the readme and disabling the exit button, greying it out and making so that it can’t be pressed. For context, the game can only run on the web, and the exit button just doesn’t do anything useful on the web. I also customized the itch.io page (which hasn’t been released yet but you can access it here via secret url). It looks kinda decent (i just copied the game’s color scheme)
Final Devlog Before Ship I’m done stalling. Every time I’ve shipped a project for Stardance, I’ve stalled for like a week trying to make sure it’s good enough. This time, I’ve stalled for more than a month. I’ve even shipped 2 separate projects to different Hack Club events in the time that this project has technically been “good enough”. Essentially, I just worked on the README a bit more. I did manage to fix a few errors. For example, I had a few spelling and grammar errors. I also added a bit more detail to the readme.
Ships:
I made a tic-tac-cube game where you can rotate the board like a Rubik’s cube! It was made in C++ with Raylib without a game engine, and only uses 2 libraries, Raylib, and Nlohmann-JSON. I’m really proud of the way I coded the entire GUI, buttons, board, and bot. Features Tic-Tac-Toe game on a Rubik’s cube Singleplayer mode against computer Local multiplayer against another player (or yourself) Main menu & help page. I coded the bot to be somewhat intelligent, and will actively try to block your attacks and create it’s own ones. It can turn the cube just like you, and will rotate the cube to get out of a pickle. This is my first C++ project, and my first experience with languages that have to be compiled before running. It was honestly really fun to make. I put a LOT of effort into this game, over 140 hours....
|
1792648449.6441333
|
1792734849.6441333
|
|
5067
|
Nest.APStudy by @derek_chen | Stardance
|
derek_chen
|
4
|
145.97
|
1964
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-08-14T17:33:45Z", "hours": 49.0, "multiplier": 14.1, "payout": 682, "blessing": "neutral", "text": "Thank you all for the feedback from my first three ships. I have focused more on the codebase and bug fixes. All the bug/issues from my last ship are addressed now! Shoutout to the people who put their time to bug find :) To those who review this ship, please share any issues you had with the website, whether it be a broken link or something simply looking bad.", "repo": "https://github.com/SimpleLemon/nest.apstudy", "demo": "https://nest.apstudy.org"}, {"ship_num": 2, "ts": "2026-07-28T20:25:20Z", "hours": 38.0, "multiplier": 17.18, "payout": 642, "blessing": "neutral", "text": "Thank you all for the feedback from my first two ships. I have focused more on detailed devlogs and small bug fixes. This ship’s focus was on general UI/UX improvements as well as a new /focus section. To those who review my 3rd ship, please share any issues you had with the website, whether it be a broken link or something simply looking bad.", "repo": "https://github.com/SimpleLemon/nest.apstudy", "demo": "https://nest.apstudy.org"}, {"ship_num": 3, "ts": "2026-07-01T18:50:16Z", "hours": 24.0, "multiplier": 15.16, "payout": 360, "blessing": "neutral", "text": "With this ship, I have drastically updated the Notes editor from a simple single-user editor into a writing workspace with block tools, rich paste normalization, and advanced features such as a print pipeline and expanded keyboard shortcuts. Sharing features such as public links, per-user shares, and a read-only view; performance optimization; accessibility (WCAG 2.2) compliance; and very, very extensive testing. Other minor fixes and admin panel changes were also included. https://nest.apstudy.org/join Let me know what you think! Please feel free to leave any suggestions and let me know of any bugs you find :)", "repo": "https://github.com/SimpleLemon/nest.apstudy", "demo": "https://nest.apstudy.org"}, {"ship_num": 4, "ts": "2026-06-16T03:46:40Z", "hours": 16.0, "multiplier": 18.6, "payout": 280, "blessing": "neutral", "text": "An Academic Command Center From now on, your day starts in one place! Features: central dashboard, calendar, tasks, notes, files, and a chat system. Course planning section specifically for Emory University students. Students can save courses, track seats, and simulate their course schedule. Build YOUR workspace today! https://nest.apstudy.org/join -# It solves my problems & reduces my stress. Hopefully it’ll solve yours too :)", "repo": "https://github.com/SimpleLemon/nest.apstudy", "demo": "https://nest.apstudy.org"}]...
|
[{"post_id": 57811, "ts": "202 [{"post_id": 57811, "ts": "2026-08-30T03:26:44Z", "hours": 9.0933, "text": "The calendar is currently still off, but I’m almost done with a fix by utilizing timezones properly. The status of each class isn’t completely accurate right now, but it should come together soon!"}, {"post_id": 56596, "ts": "2026-08-28T12:37:29Z", "hours": 9.88, "text": "I’m working on improving the calendar UI and adding .ics share features. It’s a long process, but I’m almost there for production release. The goal is to allow for .ics calendar link creation for simulated courses."}, {"post_id": 47049, "ts": "2026-08-14T17:27:53Z", "hours": 6.72, "text": "I’m beginning an extension that pairs with Nest! I’ve started porting features over to the extension to modify my school’s Canvas page. The Stardance extension project is here: https://stardance.hackclub.com/projects/46459"}, {"post_id": 44444, "ts": "2026-08-10T06:16:00Z", "hours": 6.28, "text": "REFACTOR + BUG FIXES COMPLETE! After ~40 hours, I completed a large application-wide refactor, added collaborative note review tools, and focused heavily on reliability :) Bug Fixes Fixed calendar feed validation (you can no longer add random links) and syncing issues with read-only collaborative notes. Fixed duplicate cookie-consent dialogs and the privacy-details link (shout out to the ones who caught it in ship #3!). Fixed Nest Announcements rendering errors. Fixed chat messages failing after the first send, leaking between rooms, or appearing only after a refresh. Fixed file uploads, including CSRF handling, error responses, and cleanup after failed uploads. Fixed task priority, deadline, recurrence, and floating-menu interactions becoming unresponsive (it was pretty annoying). Fixed logout behavior (thank you to the one who caught this!) Added profile length validation (thank you to the one who caught this in Ship 3!) Improved GitHub authentication diagnostics and callback/session handling (again thanks to the one who caught it in ship 3!!). Improvements Split large chat, notes, calendar, dashboard, file, onboarding, and admin systems into focused modules and services so they can be tested and changed independently. Reworked chat synchronization to be more stable. Reduced duplicate chat, presence, notification, and database work. Consolidated templates and shared frontend styling into a more consistent design (yay!). Major Features Added inline comments to shared notes, including ‘anchored’ discussions, editing and deletion, review events, and suggestions. Added administrative calendar-feed health reporting so broken or unsafe subscriptions are easier to diagnose (for admins). **Major architectural refactor across 200+ files!! https://nest.apstudy.org/ Thanks for reading!"}, {"post_id": 43490, "ts": "2026-08-08T19:48:13Z", "hours": 10.2058, "text": "Refactor continues…."}, {"post_id": 41258, "ts": "2026-08-05T11:15:47Z", "hours": 6.9658, "text": "Should be done in 4-5 days :) This will fix the bugs a lot of Ship 3’s reviewers mentioned"}, {"post_id": 39683, "ts": "2026-08-02T20:21:26Z", "hours": 9.7067, "text": "Still working! I’ll drop a more detailed summary later."}, {"post_id": 38452, "ts": "2026-07-31T20:28:56Z", "hours": 8.7231, "text": "Began a heavy code refactor today. I’ll give a very detailed refactor summary after it is complete!"}, {"post_id": 36601, "ts": "2026-07-28T20:22:34Z", "hours": 9.0275, "text": "Bug Fixes Fixed dashboard loading-grid alignment. Improved calendar week rendering for events that span multiple days or time slots. Refined course availability tracking controls. Improved invite redirects and management flows. Improvements Expanded Focus Mode with playlist composer improvements, routine selection, playlist gestures, resizable music panels, and better playback feedback. Deferred optional Focus Mode runtimes to improve loading performance. Added support for keeping completed Focus sessions visible. Improved invite-management cards and settings presentation. Major Feature* (only for myself) Added a gated Echo Show dashboard at /derek/echo for my newly jailbroken Echo! See 1st image below :)"}, {"post_id": 33263, "ts": "2026-07-23T01:14:11Z", "hours": 9.9094, "text": "This past couple days, I added Focus Mode (still a work in progress), improved navigation, and polished the frontend foundation (still got many more hours to go). Bug Fixes Fixed calendar event-editor and hidden-control behavior. Improved toast notifications and error states across the application. Improved calendar, dashboard, and notes interactions. Improvements Refined settings themes and notification controls. Added a global workspace search to the command palette with links across courses, files, calendar, and user content. Added dashboard layout editing and improved agenda/calendar interactions. Major Features Added Focus Mode with timed sessions, session history, completion sound, music playlist support (so fire since it uses less memory than Spotify), multiple playlists, Spotify playback controls, and customizable player preferences. https://nest.apstudy.org/focus"}, {"post_id": 30819, "ts": "2026-07-18T19:01:19Z", "hours": 9.5517, "text": "This past week, I focused on improving the website’s core systems, UX, and reliability. Bug Fixes Notes dragging was restored on tablets and laptops → It works again! Fixed notes page-setup popovers and improved keyboard shortcuts. Repaired browser notification subscriptions and delivery errors. Fixed settings tab visibility and improved cookie-consent controls. Hardened CSRF handling with silent token refresh for long-lived pages. Improved admin status polling and directory search. Improvements Replaced generic loaders with page-specific loading skeletons. Polished the shell, dashboard spacing, and settings layout. Improved chat profiles, member panels, composer interactions, and media previews. Added secure chat media sharing, GIF support, and attachment storage. Improved tier badge placement and presentation across the app. Major Features Users can now receive unified browser notifications, configure delivery preferences, and get reminders for individual calendar events and task deadlines!"}, {"post_id": 25606, "ts": "2026-07-09T23:46:06Z", "hours": 9.2494, "text": "My Progress Summary This past week (since my last devlog), I started with a visual and privacy redesign. Bug Fixes Note image uploads didn’t work => They work now! Calendar Validation previously would accept broken links => Fixed now! File uploads didn’t work => I can upload now! Safari kept signing me out => No more! I also improved the admin controls and security and resilience of the backend, especially with CSFR refresh handling. The loading UX was refined into page-specific skeletons (it was so cool seeing it work for the first time), but it took a few tries. The big change: Major release for tiers and quotas so that users can’t spam uploads and tier system skeleton for later. This includes admin tier controls (yay for me) and pixel assets generated by ChatGPT’s latest model 5.6 Try it here: https://nest.apstudy.org/join"}, {"post_id": 20171, "ts": "2026-07-01T18:34:05Z", "hours": 8.6836, "text": "Major Changes w/ Notes Page/Editor 1. Notes & Folder Sharing Users can now share their notes and even folders with multiple notes files with other Nest users. Public links can be generated to give everyone (even non-users) view-only access. 2. Simplified the Editor Less empty space! Now your whole screen on your laptop is the editor 3. Printing & Formatting Fixes/Updates Clicking Cmd/Ctrl + P now prints the notes page properly with appropriate colors and sizing! This actually took so many hours. Who knew modifying how a page looks for printing took this much time :( Grammarly is now more compatible and less RAM-hungry. I also optimized the notes page to reduce RAM consumption when moving between documents & website pages. Pasting now formats AI and messy text. Copy and pasting from Nest to another website looks cleaner now too (no more extra new lines for no reason like those AI websites). 4. Speed & Accessibility Changes Went with a WCAG 2.2 accessibility friendliness: people navigating can now easily run stuff through their keyboard (which is easy to open/close) Faster loading with lazy-loaded images. 5. Soon to Come: Real-Time Editing + Editor Sharing Access Who knows, it may just be more optimized than Google Docs (with fewer features, of course). It will have logic to view other people’s cursor positions & email invitations with roles (viewing, suggesting, editing). And a 30-day version history timeline. Try it yourself! https://nest.apstudy.org/notes"}, {"post_id": 15360, "ts": "2026-06-24T23:37:30Z", "hours": 4.1522, "text": "Admin Analytics Page I’m getting users! But who knew making an analytics page was so complex? I’m literally using Google Analytics API, which means I’m not hardcoding JavaScript tracking. Rather, I’m outputting the data into an interactive UI. Fun fact: 72% of users are high schoolers! https://nest.apstudy.org"}, {"post_id": 13971, "ts": "2026-06-22T22:38:21Z", "hours": 7.2628, "text": "Notes Page Design + UI You always see Google Docs and be like, “That’s a simple UI.” 😭 Or at least that’s what I thought until I tried creating a notes UI similar to both Notion & Google Docs. The goal was to make a lighter, simpler docs page that I can share easily. While, unfortunately, there are no share features yet, the toolbar and hover features are slowly coming together! 😄 Give it a try and let me know what you think: https://nest.apstudy.org/notes"}, {"post_id": 11099, "ts": "2026-06-18T04:43:46Z", "hours": 1.5686, "text": "Improved Course Search UI Changes: More results show now More filters and more details in the saved json Faster pings Seats display on the course preview Features: Simulate Courses Track courses that aren’t in your simulated schedule Recieve Emails when a class opens up Add as many courses to tracking as you wish Unfortunately, this is still limited to only Emory University students because the course pulling takes a lot of time and effort. https://nest.apstudy.org/courses"}, {"post_id": 10781, "ts": "2026-06-17T18:09:38Z", "hours": 2.69, "text": "Backend + Sidebar Fixes There were a good few small UI bugs that hurt the user experience. I went through and fixed a bunch, especially the sidebar issue that has been there for weeks. (The admin button is completely backend and won’t show for any of the other users) https://nest.apstudy.org/dashboard"}, {"post_id": 9697, "ts": "2026-06-16T03:26:14Z", "hours": 3.9828, "text": "Admin Backend & Basic Frontend Updates! I have offically gotten to a stage where I can finally start promoting and having people become early users. Nest.APStudy is a web platform for student course planning and productivity, combining local Emory course data with user accounts, dashboards, calendars, tasks, notes, chat, file sharing, and related admin tools. User data is stored in VPS-hosted SQLite databases, with Appwrite handling authentication and file storage. I spent a few more hours working on the backend changes and setting myself up to be in a position to quickly find and repair bugs by connecting the hosting VPS to my admin portal securely. Take a look and let me know what you think! :)"}, {"post_id": 8289, "ts": "2026-06-13T19:42:50Z", "hours": 0.9914, "text": "Backend Updates Updated the /admin page and transferred my database away from Appwrite towards a locally hosted SQLite on my VPS. Please take a look at https://nest.apstudy.org All the features are looking really solid now!"}, {"post_id": 7751, "ts": "2026-06-12T23:01:22Z", "hours": 2.7614, "text": "Created landing page for my Productivity Web Platform Added hero image, animations, showcases of all the website features, etc. I added some avengers easter eggs if you can find them ;) As for the actual platform, it is currently not fully functioning because I hit the database limits on my GitHub education plan. I may be locally hosting the database soon. Check out the website at https://nest.apstudy.org and feel free to leave me any comments/suggestions/hate! I appreciate all feedback :)"}, {"post_id": 2252, "ts": "2026-06-04T17:22:02Z", "hours": 0.9125, "text": "Calendar Page Creation & Polish! https://nest.apstudy.org/calendar Weekly view: 7-column time grid starting with Sunday 24 hour scrollable view that auto-scrolls to 7:30 AM Stickied all-day row (if all-day events are present) for multi-day/all-day events! Monthly View: 6-week grids with leading and trailing days from the adjacent months (less visible) Multi-day events are shown as spanning bars with row packing Options to share calendar (lots of configuration), calendar visibility, previous/next, and easy toggle between weekly and monthly views."}, {"post_id": 1587, "ts": "2026-06-03T18:22:11Z", "hours": 7.4042, "text": "Nest.APStudy is my summer coding project that I started right before AP testing. Yeah, I know… great timing. But I couldn’t wait to start working! 7 APs got nothing on me! Let me enjoy coding and discover the wonderful digital world of cyber, hardware, software, and AI :) Soon to be up at https://nest.apstudy.org"}]...
|
Title: Nest.APStudy by @derek_chen | Stardance
De Title: Nest.APStudy by @derek_chen | Stardance
Devlogs:
Nest.APStudy is my summer coding project that I started right before AP testing. Yeah, I know… great timing. But I couldn’t wait to start working! 7 APs got nothing on me! Let me enjoy coding and discover the wonderful digital world of cyber, hardware, software, and AI :) Soon to be up at https://nest.apstudy.org
Calendar Page Creation & Polish! https://nest.apstudy.org/calendar Weekly view: 7-column time grid starting with Sunday 24 hour scrollable view that auto-scrolls to 7:30 AM Stickied all-day row (if all-day events are present) for multi-day/all-day events! Monthly View: 6-week grids with leading and trailing days from the adjacent months (less visible) Multi-day events are shown as spanning bars with row packing Options to share calendar (lots of configuration), calendar visibility, previous/next, and easy toggle between weekly and monthly views.
Created landing page for my Productivity Web Platform Added hero image, animations, showcases of all the website features, etc. I added some avengers easter eggs if you can find them ;) As for the actual platform, it is currently not fully functioning because I hit the database limits on my GitHub education plan. I may be locally hosting the database soon. Check out the website at https://nest.apstudy.org and feel free to leave me any comments/suggestions/hate! I appreciate all feedback :)
Backend Updates Updated the /admin page and transferred my database away from Appwrite towards a locally hosted SQLite on my VPS. Please take a look at https://nest.apstudy.org All the features are looking really solid now!
Admin Backend & Basic Frontend Updates! I have offically gotten to a stage where I can finally start promoting and having people become early users. Nest.APStudy is a web platform for student course planning and productivity, combining local Emory course data with user accounts, dashboards, calendars, tasks, notes, chat, file sharing, and related admin tools. User data is stored in VPS-hosted SQLite databases, with Appwrite handling authentication and file storage. I spent a few more hours working on the backend changes and setting myself up to be in a position to quickly find and repair bugs by connecting the hosting VPS to my admin portal securely. Take a look and let me know what you think! :)
Backend + Sidebar Fixes There were a good few small UI bugs that hurt the user experience. I went through and fixed a bunch, especially the sidebar issue that has been there for weeks. (The admin button is completely backend and won’t show for any of the other users) https://nest.apstudy.org/dashboard
Improved Course Search UI Changes: More results show now More filters and more details in the saved json Faster pings Seats display on the course preview Features: Simulate Courses Track courses that aren’t in your simulated schedule Recieve Emails when a class opens up Add as many courses to tracking as you wish Unfortunately, this is still limited to only Emory University students because the course pulling takes a lot of time and effort. https://nest.apstudy.org/courses
Notes Page Design + UI You always see Google Docs and be like, “That’s a simple UI.” 😭 Or at least that’s what I thought until I tried creating a notes UI similar to both Notion & Google Docs. The goal was to make a lighter, simpler docs page that I can share easily. While, unfortunately, there are no share features yet, the toolbar and hover features are slowly coming together! 😄 Give it a try and let me know what you think: https://nest.apstudy.org/notes
Admin Analytics Page I’m getting users! But who knew making an analytics page was so complex? I’m literally using Google Analytics API, which means I’m not hardcoding JavaScript tracking. Rather, I’m outputting the data into an interactive UI. Fun fact: 72% of users are high schoolers! https://nest.apstudy.org
Major Changes w/ Notes Page/Editor 1. Notes & Folder Sharing Users can now share their notes and even folders with multiple notes files with other Nest users. Public links can be generated to give everyone (even non-users) view-only access. 2. Simplified the Editor Less empty space! Now your whole screen on your laptop is the editor 3. Printing & Formatting Fixes/Updates Clicking Cmd/Ctrl + P now prints the notes page properly with appropriate colors and sizing! This actually took so many hours. Who knew modifying how a page looks for printing took this much time :( Grammarly is now more compatible and less RAM-hungry. I also optimized the notes page to reduce RAM consumption when moving between documents & website pages. Pasting now formats AI and messy text. Copy and pasting from Nest to another website looks cleaner now too (no more extra new lines for no reason like those AI websites). 4. Speed & Accessibility Changes Went with a WCAG 2.2 accessibility friendliness: people navigating can now easily run stuff through their keyboard (which is easy to open/close) Faster loading with lazy-loaded images. 5. Soon to Come: Real-Time Editing + Editor Sharing Access Who knows, it may just be more optimized than Google Docs (with fewer features, of course). It will have logic to view other people’s cursor positions & email invitations with roles (viewing, suggesting, editing). And a 30-day version history timeline. Try it yourself! https://nest.apstudy.org/notes
My Progress Summary This past week (since my last devlog), I started with a visual and privacy redesign. Bug Fixes Note image uploads didn’t work => They work now! Calendar Validation previously would accept broken links => Fixed now! File uploads didn’t work => I can upload now! Safari kept signing me out => No more! I also improved the admin controls and security and resilience of the backend, especially with CSFR refresh handling. The loading UX was refined into page-specific skeletons (it was so cool seeing it work for the first time), but it took a few tries. The big change: Major release for tiers and quotas so that users can’t spam uploads and tier system skeleton for later. This includes admin tier controls (yay for me) and pixel assets generated by ChatGPT’s latest model 5.6 Try it here: https://nest.apstudy.org/join
This past week, I focused on improving the website’s core systems, UX, and reliability. Bug Fixes Notes dragging was restored on tablets and laptops → It works again! Fixed notes page-setup popovers and improved keyboard shortcuts. Repaired browser notification subscriptions and delivery errors. Fixed settings tab visibility and improved cookie-consent controls. Hardened CSRF handling with silent token refresh for long-lived pages. Improved admin status polling and directory search. Improvements Replaced generic loaders with page-specific loading skeletons. Polished the shell, dashboard spacing, and settings layout. Improved chat profiles, member panels, composer interactions, and media previews. Added secure chat media sharing, GIF support, and attachment storage. Improved tier badge placement and presentation across the app. Major Features Users can now receive unified browser notifications, configure delivery preferences, and get reminders for individual calendar events and task deadlines!
This past couple days, I added Focus Mode (still a work in progress), improved navigation, and polished the frontend foundation (still got many more hours to go). Bug Fixes Fixed calendar event-editor and hidden-control behavior. Improved toast notifications and error states across the application. Improved calendar, dashboard, and notes interactions. Improvements Refined settings themes and notification controls. Added a global workspace search to the command palette with links across courses, files, calendar, and user content. Added dashboard layout editing and improved agenda/calendar interactions. Major Features Added Focus Mode with timed sessions, session history, completion sound, music playlist support (so fire since it uses less memory than Spotify), multiple playlists, Spotify playback controls, and customizable player preferences. https://nest.apstudy.org/focus
Bug Fixes Fixed dashboard loading-grid alignment. Improved calendar week rendering for events that span multiple days or time slots. Refined course availability tracking controls. Improved invite redirects and management flows. Improvements Expanded Focus Mode with playlist composer improvements, routine selection, playlist gestures, resizable music panels, and better playback feedback. Deferred optional Focus Mode runtimes to improve loading performance. Added support for keeping completed Focus sessions visible. Improved invite-management cards and settings presentation. Major Feature* (only for myself) Added a gated Echo Show dashboard at /derek/echo for my newly jailbroken Echo! See 1st image below :)
Began a heavy code refactor today. I’ll give a very detailed refactor summary after it is complete!
Still working! I’ll drop a more detailed summary later.
Should be done in 4-5 days :) This will fix the bugs a lot of Ship 3’s reviewers mentioned
Refactor continues….
REFACTOR + BUG FIXES COMPLETE! After ~40 hours, I completed a large application-wide refactor, added collaborative note review tools, and focused heavily on reliability :) Bug Fixes Fixed calendar feed validation (you can no longer add random links) and syncing issues with read-only collaborative notes. Fixed duplicate cookie-consent dialogs and the privacy-details link (shout out to the ones who caught it in ship #3!). Fixed Nest Announcements rendering errors. Fixed chat messages failing after the first send, leaking between rooms, or appearing only after a refresh. Fixed file uploads, including CSRF handling, error responses, and cleanup after failed uploads. Fixed task priority, deadline, recurrence, and floating-menu interactions becoming unresponsive (it was pretty annoying). Fixed logout behavior (thank you to the one who caught this!) Added profile length validation (thank you to the one who caught this in Ship 3!) Improved GitHub authentication diagnostics and callback/session handling (again thanks to the one who caught it in ship 3!!). Improvements Split large chat, notes, calendar, dashboard, file, onboarding, and admin systems into focused modules and services so they can be tested and changed independently. Reworked chat synchronization to be more stable. Reduced duplicate chat, presence, notification, and database work. Consolidated templates and shared frontend styling into a more consistent design (yay!). Major Features Added inline comments to shared notes, including ‘anchored’ discussions, editing and deletion, review events, and suggestions. Added administrative calendar-feed health reporting so broken or unsafe subscriptions are easier to diagnose (for admins). **Major architectural refactor across 200+ files!! https://nest.apstudy.org/ Thanks for reading!
I’m beginning an extension that pairs with Nest! I’ve started porting features over to the extension to modify my school’s Canvas page. The Stardance extension project is here: https://stardance.hackclub.com/projects/46459
I’m working on improving the calendar UI and adding .ics share features. It’s a long process, but I’m almost there for production release. The goal is to allow for .ics calendar link creation for simulated courses.
The calendar is currently still off, but I’m almost done with a fix by utilizing timezones properly. The status of each class isn’t completely accurate right now, but it should come together soon!
Ships:
An Academic Command Center From now on, your day starts in one place! Features: central dashboard, calendar, tasks, notes, files, and a chat system. Course planning section specifically for Emory University students. Students can save courses, track seats, and simulate their course schedule. Build YOUR workspace today! https://nest.apstudy.org/join -# It solves my problems & reduces my stress. Hopefully it’ll solve yours too :)
With this ship, I have drastically updated the Notes editor from a simple single-user editor into a writing workspace with block tools, rich paste normalization, and advanced features such as a print pipeline and expanded keyboard shortcuts. Sharing features such as public links, per-user shares, and a read-only view; performance optimization; accessibility (WCAG 2.2) compliance; and very, very extensive testing. Other minor fixes and admin panel changes were also included. https://nest.apstudy.org/join Let me know what you think! Please feel free to leave any suggestions and let me know of any bugs you find :)
Thank you all for the feedback from my first two ships. I have focused more on detailed devlogs and small bug fixes. This ship’s focus was on general UI/UX improvements as well as a new /focus section. To those who review my 3rd ship, please share any issues you had with the website, whether it be a broken link or something simply looking bad.
Thank you all for the feedback from my first three ships. I have focused more on the codebase and bug fixes. All the bug/issues from my last ship are addressed now! Shoutout to the people who put their time to bug find :) To those who review this ship, please share any issues you had with the website, whether it be a broken link or something simply looking bad....
|
1792832214.1565225
|
1792918614.1565225
|
|
22029
|
Sentinel: Autonomous Exploration Rover by @Sriyan Sentinel: Autonomous Exploration Rover by @Sriyan | Stardance...
|
Sriyan
|
1
|
146.0
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-30T03:15:27Z", "hours": 146.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "I made a robot controller and the code responding for it although I couldn’t finish the robot itself I feel like this Is worth the effort I put into it thanks, Sriyan", "repo": "https://github.com/BigestBrainestbozo/Sentinal-robot-website-and-code", "demo": ""}]...
|
[{"post_id": 75433, "ts": "202 [{"post_id": 75433, "ts": "2026-09-30T02:29:18Z", "hours": 20.4072, "text": "Devlog — Motor Control System Time: ~7 Hours I wasn’t able to finish building the physical robot, so this year I’ve been focusing more on the coding side of the project. During these 7 hours, I worked on the robot’s motor control system, including movement, motor power, direction, and stopping. I also spent time testing and debugging the code so that when I return to the physical robot, I’ll already have a stronger software foundation ready. Next I’m going to keep developing and improving the code this year, and eventually return to finishing and integrating it with the physical robot."}, {"post_id": 55358, "ts": "2026-08-27T04:21:04Z", "hours": 5.8667, "text": "––Devlog 17–– Continuing Sentinel’s Retrace System Today I continued working on Sentinel’s autonomous retrace system. The goal of this system is to give it a way to recover when autonomous navigation leads it into a bad situation. Instead of stopping, it will eventually be able to remember its recent route, retrace it, look for another path, and only return control to the user if autonomous recovery fails. Recording Autonomous Movement I continued connecting it’s existing autonomous navigation to the movement-history system. it can store recent movements using: rememberMovement ()"}, {"post_id": 54250, "ts": "2026-08-25T04:19:16Z", "hours": 14.075, "text": "––Devlog 16–– Testing Sentinel in the Real World Yesterday I focused on just testing it over obstacles I’ve spent a lot of recent sessions improving the autonomous navigation system, failsafe logic, sensor handling, mission statistics, and the beginning of the retrace system. Physical Testing I spent the session working with Sentinel’s real hardware and observing how the different systems behave outside of the Arduino IDE. I focused on: Motor movement Servo movement Ultrasonic sensor behavior Sensor positioning Current wiring Rover movement Existing manual controls Existing autonomous behavior"}, {"post_id": 52469, "ts": "2026-08-22T16:24:33Z", "hours": 4.9, "text": "––Devlog 15–– Remembering its Path Today I started working on a much more advanced recovery system for the rover. Until now, FAILSAFE has mainly been designed to stop the rover when autonomous navigation runs into a serious problem. I don’t want stopping to always be the rovers first and only solution. Instead, I want Sentinel to eventually remember where it came from, retrace part of its path, and search for another route. The New Recovery Idea The recovery system I’m building is designed to eventually work like this: Dead End? I v FAILSAFE I v Retrace Recent Path I v Scan Surroundings Again I v Try Another Route I v No Safe Route? I v Switch to Manual Mode"}, {"post_id": 51305, "ts": "2026-08-20T21:38:39Z", "hours": 5.5142, "text": "—Devlog 14— Making it more reliable today I continue improving the autonomous navigation system work better by making a better failsafe. Sensor Failures I improved sentinels ultrasonic sensor failure handling/ the rover now treats different situations respectively not just backing up when sensor failsafe now it will retrace its path and go a different way if there is no other way the rover will automatically switch to manual mode and your device will do a cool animation change in the schedule since school started I am trying hard to balance 6 hours of school and 5 hours of work each day, so now physical and catch up days are on weekends due to having no school."}, {"post_id": 45366, "ts": "2026-08-11T19:31:55Z", "hours": 4.9944, "text": "–Devlog 13– Finishing Sentinel’s Failsafe System Today I continued working on Sentinel’s autonomous safety system. The main goal was to make FAILSAFE more useful instead of having it simply stop the rover without explaining what happened."}, {"post_id": 44450, "ts": "2026-08-10T06:23:59Z", "hours": 13.1775, "text": "––Devlog 12–– Making Sentinel Safer Today I focused on improving autonomous navigation and making the rover safer when something goes maybe might go wrong. A lot of today’s work was about making sure Sentinel doesn’t just assume that every sensor reading is correct or that every direction is safe. Smarter Path Decisions I added a minimum turning clearance to the rover’s settings. Before this, Sentinel could scan both directions and simply choose whichever side had more space. For example: left: 8 cm right: 12 cm the right side has more space, but 12 cm still isn’t enough room to safely turn. Now it checks whether a direction actually has enough clearance before choosing it. If both directions are clear, it chooses the side with more room. If only one direction is clear, it chooses that direction. If neither direction is clear, it knows that it needs to try something else. gn"}, {"post_id": 43773, "ts": "2026-08-09T06:19:22Z", "hours": 7.4453, "text": "———––Devlog 11———— I made it to Colorado yeah Failsafe I decided to make the autonomous mode safer rather than faster. the purpose of the failsafe is if something is to ever go wrong it will know what to do. Smart Path Decision well you heard the title, well basically what I did is that it can tell the difference between: A clear path A path that is too close to an obstacle A failed sensor reading Both paths being blocked I also added a minimum turning distance so it doesn’t choose one side for being like 1 cm better than the other. Example: left: 11cm right 12cm"}, {"post_id": 42656, "ts": "2026-08-07T14:31:52Z", "hours": 5.2392, "text": "———––Devlog 10———— Improving Sentinel’s Brain And Hardware Today I finally did a hardware and software day I spent the digital part fixing my devlogs and coding the place holder awareness. Software Progress I continued working on the autonomous navigation system like: Tracking servo current position Improved logic for left, center, and right Remembering sensor readings Physical Work My solder wire is leaded im cooked I didnt like my solder work so I tried again but this time I watched a video and it said leaded is dangerous so yeah but I connected 1 wire cut in half\\ Important Info im going to colorado today for 10 days but 3 of them Im going to be in mountain so no work Thanks for following Project Sentinel!"}, {"post_id": 41798, "ts": "2026-08-06T06:53:25Z", "hours": 7.4844, "text": "———––Devlog 09———— Sentinel’s Software Gets Smarter Today I focused mainly on improving Sentinel’s software and project documentation . The rover is not physically ready for full testing yet, so I used the digital work session to organize the code, fix errors, add useful debugging features, and improve how I present the project. Software Progress I expanded Sentinel’s Arduino Mega code with several new systems: Manual and autonomous driving modes Emergency stop command Configurable rover settings Obstacle detection and stopping Reverse-and-turn behavior Servo-based left, center, and right scanning Mission statistics Current rover status reporting Structured communication messages TB6612 motor-driver control functions The autonomous system is designed to follow this sequence: Drive forward Detect an obstacle Stop Reverse Scan left, center, and right Compare the available paths Choose the clearer direction Turn Continue driving Mission Statistics I added a command that can report information such as: Mission runtime Number of obstacles detected Number of left turns Number of right turns Number of successful distance readings I also added a current-status command so I can check Sentinel’s mode, state, movement command, and latest sensor reading while debugging. 📡 Communication Protocol I standardized the messages Sentinel sends through the Serial Monitor. Examples include: MODE:MANUAL MODE:AUTONOMOUS STATE:DRIVING STATE:REVERSING STATE:SCANNING_LEFT CMD:FORWARD CMD:STOP DIST:32.4"}, {"post_id": 40925, "ts": "2026-08-04T19:19:32Z", "hours": 14.7436, "text": "———––Devlog 08———— Making The Navigation Brain Toda I focused on the software for Sentinel. instead of working on hardware all day I switch to digital to make the user interface easy and intuitive for everyone(me) to use without any brain power. What I coded I added code for : The stop distance before obstacles Sensor-reading interval duration for turning and reversing and movement speed changes I got these from looking at my robot vac believe it or not. Preparations I began preparing for the motors because i have to solder them to the Arduino and im a noob at it so i made self drving mode and manual mode with placeholders What I Learned those little disks roaming around houses are hard to code kudos to the engineers, but I think that I learned that sensor awarness is hard and that anyways, Thanks for listening to my thing and following my project."}, {"post_id": 39172, "ts": "2026-08-01T23:01:50Z", "hours": 10.3956, "text": "———––Devlog 07———— Sentinel’s Web Controller Is Fixed Today I fixed a major part of the website which was the mobile part not being able to work properly Desktop Controls On a computer, sentinel can be controlled using the keyboard. The main controls are (for now): W - Forward A - Left S - Backward D - Right Mobile Controls For phones, I designed a different interface, instead of trying to use small buttons to control I made a joystick . Device Detection The website automatically provides better control method depending on what device you are using. Thanks for following Project Sentinel ! We’re getting closer to actually driving this thing."}, {"post_id": 37934, "ts": "2026-07-30T23:16:10Z", "hours": 9.9833, "text": "—————— Devlog 06 —————— Building Sentinel’s Eyes & Control Center Over the past few days, I worked on both the physical rover and its control system. What I Finished I completed the custom mount for Sentinel’s ultrasonic sensors. The mount holds the sensors securely and prepares them to be mounted on the servo, allowing Sentinel to scan left, center, and right while navigating. Website Progress I also spent time building Sentinel’s web controller. So far I have created: 🖥️ Desktop version Mobile version Both versions are coming together nicely. The only problem is that the mobile version isn’t displaying correctly on my phone yet, so I’ll show it off in the next devlog after I fix it. What I Learned This project is becoming much bigger than just building a rover. I’m now working on: CAD 3D printing Web development C++ Electronics Hardware integration It’s exciting seeing all of these skills come together in one project. Next Mission Fix the mobile website layout Finish the web controller Mount the ultrasonic sensor onto the servo Continue building Sentinel’s autonomous navigation system Thanks for following Project Sentinel ! Every update gets me one step closer to bringing Sentinel to life."}, {"post_id": 36717, "ts": "2026-07-28T23:13:11Z", "hours": 4.4978, "text": "———––Devlog 05———— Learning CAD - Day 2 Today I continued learning CAD so I eventually design custom parts for Sentinel . What I Learned Its hard. also refined my skills and made some medium difficulty items. Thanks for following Project Sentinel!"}, {"post_id": 36078, "ts": "2026-07-27T22:48:14Z", "hours": 11.8211, "text": "———––Devlog 04———— Learning CAD After being away for a while, I finally made it home! Now that I access to all my hardware again, I decided to make a mount for my servo and sensor and for the battery. Why I’m Learning CAD The rover needs some mount for: Ultrasonic Sensor Servo Battery Other electronics What I’m Working On My first goal is to design a bracket that can mount the ultrasonic sensor on top of the servo. That setup will allow Sentinel to: Look left Look forward Look right Compare distances Choose the best path What I’m Learning This is my first time trying a 3d modeling software, so Im going to start off with the basics. Target for tomorrow Make simple 3d shapes Build a servo mount Maybe print Thanks for following Project Sentinel!"}, {"post_id": 33733, "ts": "2026-07-23T21:42:37Z", "hours": 4.36, "text": "———––Devlog 03———— Information It’s definitely been longer than I planned since my last update . I was on vacation for a while, and I honestly lost more time than I wanted to. Instead of letting that stop the project, I decided to get back on track by focusing on something that will help Sentinel in the long run: learning C++ . What I’ve Been Doing While I haven’t been home to work on the hardware, I’ve been spending my time learning the fundamentals of C++. Some of the topics I’ve been working through includes: Variables Loops Functions Enums Classes General programming concepts I’ve attached a few screenshots showing what I’ve been learning. The Plan Since I lost some time during vacay, I going to have to do 4 hours and 32 minutes a day until I reach my goal. It won’t be easy, but I know it’ll be worth it when Sentinel finally comes together. Progress Even though there hasn’t been much hardware progress yet, I believe learning C++ now will save me a lot of time later because I’ll be writing and understanding my own rover software instead of relying on tutorials. Next Mission Continue learning C++ Return home Start assembling Sentinel Begin writing the rover’s software Thanks for following Project Sentinel. The build will finally start picking up speed soon!"}, {"post_id": 14189, "ts": "2026-06-23T05:44:45Z", "hours": 1.0669, "text": "———––Devlog 02———— Preparing For Sentinel Welcome back to another devlog! While I’m still waiting for the rest of Sentinel’s parts to arrive (they should be here by the end of June), I’ve decided to spend this time learning C++ . Instead of relying only on the Arduino examples and documentation, I want to understand how everything works so I can build different project by myself. Current Plan While waiting for the hardware, I’m focusing on learning: Variables Functions Loops Classes Enums General C++ programming My goal is to write as much of Sentinel’s software myself as possible instead of just copying code. Hardware Update As I said before Most of the remaining parts are expected to arrive near the end of June . Once everything arrives, I’ll be able to start assembling the rover and putting everything I’ve learned into practice. Target For Tomorrow -Continue learning C++ Wait for the remaining hardware Begin assembling Sentinel when the parts arrive Thanks so much for following Project Sentinel! Every bit of support motivates me to keep improving."}, {"post_id": 14046, "ts": "2026-06-23T00:51:47Z", "hours": 0.3708, "text": "———––Devlog 01———— Giving Sentinel Its Eyes Today I connect the ultrasonic sensors that will act as the eyes of Sentinel What I Added Ultrasonic distance sensors Front-facing sensor layout Very basic obstacle detection software Why Ultrasonic Sensors? I considered using cameras, but they were outside my budget and would have made the project more complicated. Target for tomorrow Test distance readings Connect the sensor to the Arduino Mega Start obstacle-avoidance log"}]...
|
Title: Sentinel: Autonomous Exploration Rover by @ Title: Sentinel: Autonomous Exploration Rover by @Sriyan | Stardance
Devlogs:
———––Devlog 01———— Giving Sentinel Its Eyes Today I connect the ultrasonic sensors that will act as the eyes of Sentinel What I Added Ultrasonic distance sensors Front-facing sensor layout Very basic obstacle detection software Why Ultrasonic Sensors? I considered using cameras, but they were outside my budget and would have made the project more complicated. Target for tomorrow Test distance readings Connect the sensor to the Arduino Mega Start obstacle-avoidance log
———––Devlog 02———— Preparing For Sentinel Welcome back to another devlog! While I’m still waiting for the rest of Sentinel’s parts to arrive (they should be here by the end of June), I’ve decided to spend this time learning C++ . Instead of relying only on the Arduino examples and documentation, I want to understand how everything works so I can build different project by myself. Current Plan While waiting for the hardware, I’m focusing on learning: Variables Functions Loops Classes Enums General C++ programming My goal is to write as much of Sentinel’s software myself as possible instead of just copying code. Hardware Update As I said before Most of the remaining parts are expected to arrive near the end of June . Once everything arrives, I’ll be able to start assembling the rover and putting everything I’ve learned into practice. Target For Tomorrow -Continue learning C++ Wait for the remaining hardware Begin assembling Sentinel when the parts arrive Thanks so much for following Project Sentinel! Every bit of support motivates me to keep improving.
———––Devlog 03———— Information It’s definitely been longer than I planned since my last update . I was on vacation for a while, and I honestly lost more time than I wanted to. Instead of letting that stop the project, I decided to get back on track by focusing on something that will help Sentinel in the long run: learning C++ . What I’ve Been Doing While I haven’t been home to work on the hardware, I’ve been spending my time learning the fundamentals of C++. Some of the topics I’ve been working through includes: Variables Loops Functions Enums Classes General programming concepts I’ve attached a few screenshots showing what I’ve been learning. The Plan Since I lost some time during vacay, I going to have to do 4 hours and 32 minutes a day until I reach my goal. It won’t be easy, but I know it’ll be worth it when Sentinel finally comes together. Progress Even though there hasn’t been much hardware progress yet, I believe learning C++ now will save me a lot of time later because I’ll be writing and understanding my own rover software instead of relying on tutorials. Next Mission Continue learning C++ Return home Start assembling Sentinel Begin writing the rover’s software Thanks for following Project Sentinel. The build will finally start picking up speed soon!
———––Devlog 04———— Learning CAD After being away for a while, I finally made it home! Now that I access to all my hardware again, I decided to make a mount for my servo and sensor and for the battery. Why I’m Learning CAD The rover needs some mount for: Ultrasonic Sensor Servo Battery Other electronics What I’m Working On My first goal is to design a bracket that can mount the ultrasonic sensor on top of the servo. That setup will allow Sentinel to: Look left Look forward Look right Compare distances Choose the best path What I’m Learning This is my first time trying a 3d modeling software, so Im going to start off with the basics. Target for tomorrow Make simple 3d shapes Build a servo mount Maybe print Thanks for following Project Sentinel!
———––Devlog 05———— Learning CAD - Day 2 Today I continued learning CAD so I eventually design custom parts for Sentinel . What I Learned Its hard. also refined my skills and made some medium difficulty items. Thanks for following Project Sentinel!
—————— Devlog 06 —————— Building Sentinel’s Eyes & Control Center Over the past few days, I worked on both the physical rover and its control system. What I Finished I completed the custom mount for Sentinel’s ultrasonic sensors. The mount holds the sensors securely and prepares them to be mounted on the servo, allowing Sentinel to scan left, center, and right while navigating. Website Progress I also spent time building Sentinel’s web controller. So far I have created: 🖥️ Desktop version Mobile version Both versions are coming together nicely. The only problem is that the mobile version isn’t displaying correctly on my phone yet, so I’ll show it off in the next devlog after I fix it. What I Learned This project is becoming much bigger than just building a rover. I’m now working on: CAD 3D printing Web development C++ Electronics Hardware integration It’s exciting seeing all of these skills come together in one project. Next Mission Fix the mobile website layout Finish the web controller Mount the ultrasonic sensor onto the servo Continue building Sentinel’s autonomous navigation system Thanks for following Project Sentinel ! Every update gets me one step closer to bringing Sentinel to life.
———––Devlog 07———— Sentinel’s Web Controller Is Fixed Today I fixed a major part of the website which was the mobile part not being able to work properly Desktop Controls On a computer, sentinel can be controlled using the keyboard. The main controls are (for now): W - Forward A - Left S - Backward D - Right Mobile Controls For phones, I designed a different interface, instead of trying to use small buttons to control I made a joystick . Device Detection The website automatically provides better control method depending on what device you are using. Thanks for following Project Sentinel ! We’re getting closer to actually driving this thing.
———––Devlog 08———— Making The Navigation Brain Toda I focused on the software for Sentinel. instead of working on hardware all day I switch to digital to make the user interface easy and intuitive for everyone(me) to use without any brain power. What I coded I added code for : The stop distance before obstacles Sensor-reading interval duration for turning and reversing and movement speed changes I got these from looking at my robot vac believe it or not. Preparations I began preparing for the motors because i have to solder them to the Arduino and im a noob at it so i made self drving mode and manual mode with placeholders What I Learned those little disks roaming around houses are hard to code kudos to the engineers, but I think that I learned that sensor awarness is hard and that anyways, Thanks for listening to my thing and following my project.
———––Devlog 09———— Sentinel’s Software Gets Smarter Today I focused mainly on improving Sentinel’s software and project documentation . The rover is not physically ready for full testing yet, so I used the digital work session to organize the code, fix errors, add useful debugging features, and improve how I present the project. Software Progress I expanded Sentinel’s Arduino Mega code with several new systems: Manual and autonomous driving modes Emergency stop command Configurable rover settings Obstacle detection and stopping Reverse-and-turn behavior Servo-based left, center, and right scanning Mission statistics Current rover status reporting Structured communication messages TB6612 motor-driver control functions The autonomous system is designed to follow this sequence: Drive forward Detect an obstacle Stop Reverse Scan left, center, and right Compare the available paths Choose the clearer direction Turn Continue driving Mission Statistics I added a command that can report information such as: Mission runtime Number of obstacles detected Number of left turns Number of right turns Number of successful distance readings I also added a current-status command so I can check Sentinel’s mode, state, movement command, and latest sensor reading while debugging. 📡 Communication Protocol I standardized the messages Sentinel sends through the Serial Monitor. Examples include: MODE:MANUAL MODE:AUTONOMOUS STATE:DRIVING STATE:REVERSING STATE:SCANNING_LEFT CMD:FORWARD CMD:STOP DIST:32.4
———––Devlog 10———— Improving Sentinel’s Brain And Hardware Today I finally did a hardware and software day I spent the digital part fixing my devlogs and coding the place holder awareness. Software Progress I continued working on the autonomous navigation system like: Tracking servo current position Improved logic for left, center, and right Remembering sensor readings Physical Work My solder wire is leaded im cooked I didnt like my solder work so I tried again but this time I watched a video and it said leaded is dangerous so yeah but I connected 1 wire cut in half\ Important Info im going to colorado today for 10 days but 3 of them Im going to be in mountain so no work Thanks for following Project Sentinel!
———––Devlog 11———— I made it to Colorado yeah Failsafe I decided to make the autonomous mode safer rather than faster. the purpose of the failsafe is if something is to ever go wrong it will know what to do. Smart Path Decision well you heard the title, well basically what I did is that it can tell the difference between: A clear path A path that is too close to an obstacle A failed sensor reading Both paths being blocked I also added a minimum turning distance so it doesn’t choose one side for being like 1 cm better than the other. Example: left: 11cm right 12cm
––Devlog 12–– Making Sentinel Safer Today I focused on improving autonomous navigation and making the rover safer when something goes maybe might go wrong. A lot of today’s work was about making sure Sentinel doesn’t just assume that every sensor reading is correct or that every direction is safe. Smarter Path Decisions I added a minimum turning clearance to the rover’s settings. Before this, Sentinel could scan both directions and simply choose whichever side had more space. For example: left: 8 cm right: 12 cm the right side has more space, but 12 cm still isn’t enough room to safely turn. Now it checks whether a direction actually has enough clearance before choosing it. If both directions are clear, it chooses the side with more room. If only one direction is clear, it chooses that direction. If neither direction is clear, it knows that it needs to try something else. gn
–Devlog 13– Finishing Sentinel’s Failsafe System Today I continued working on Sentinel’s autonomous safety system. The main goal was to make FAILSAFE more useful instead of having it simply stop the rover without explaining what happened.
—Devlog 14— Making it more reliable today I continue improving the autonomous navigation system work better by making a better failsafe. Sensor Failures I improved sentinels ultrasonic sensor failure handling/ the rover now treats different situations respectively not just backing up when sensor failsafe now it will retrace its path and go a different way if there is no other way the rover will automatically switch to manual mode and your device will do a cool animation change in the schedule since school started I am trying hard to balance 6 hours of school and 5 hours of work each day, so now physical and catch up days are on weekends due to having no school.
––Devlog 15–– Remembering its Path Today I started working on a much more advanced recovery system for the rover. Until now, FAILSAFE has mainly been designed to stop the rover when autonomous navigation runs into a serious problem. I don’t want stopping to always be the rovers first and only solution. Instead, I want Sentinel to eventually remember where it came from, retrace part of its path, and search for another route. The New Recovery Idea The recovery system I’m building is designed to eventually work like this: Dead End? I v FAILSAFE I v Retrace Recent Path I v Scan Surroundings Again I v Try Another Route I v No Safe Route? I v Switch to Manual Mode
––Devlog 16–– Testing Sentinel in the Real World Yesterday I focused on just testing it over obstacles I’ve spent a lot of recent sessions improving the autonomous navigation system, failsafe logic, sensor handling, mission statistics, and the beginning of the retrace system. Physical Testing I spent the session working with Sentinel’s real hardware and observing how the different systems behave outside of the Arduino IDE. I focused on: Motor movement Servo movement Ultrasonic sensor behavior Sensor positioning Current wiring Rover movement Existing manual controls Existing autonomous behavior
––Devlog 17–– Continuing Sentinel’s Retrace System Today I continued working on Sentinel’s autonomous retrace system. The goal of this system is to give it a way to recover when autonomous navigation leads it into a bad situation. Instead of stopping, it will eventually be able to remember its recent route, retrace it, look for another path, and only return control to the user if autonomous recovery fails. Recording Autonomous Movement I continued connecting it’s existing autonomous navigation to the movement-history system. it can store recent movements using: rememberMovement ()
Devlog — Motor Control System Time: ~7 Hours I wasn’t able to finish building the physical robot, so this year I’ve been focusing more on the coding side of the project. During these 7 hours, I worked on the robot’s motor control system, including movement, motor power, direction, and stopping. I also spent time testing and debugging the code so that when I return to the physical robot, I’ll already have a stronger software foundation ready. Next I’m going to keep developing and improving the code this year, and eventually return to finishing and integrating it with the physical robot.
Ships:
I made a robot controller and the code responding for it although I couldn’t finish the robot itself I feel like this Is worth the effort I put into it thanks, Sriyan...
|
1792853565.0659344
|
1792939965.0659344
|
|
56374
|
liteNgine by @AzzyAzimuth | Stardance
|
AzzyAzimuth
|
0
|
146.62
|
0
|
[]
|
[{"post_id": 65253, "ts": "202 [{"post_id": 65253, "ts": "2026-09-10T08:01:55Z", "hours": 7.1728, "text": "I’m cooking up a storm. The storm is leaking memory. Both sentences can be true without detracting from the skill I genuinely do not possess. My copy and paste Keys have been obliterated and my commits have become more and more loose with their terms due to the decreasing amounts of awareness I possess about what the heck I’m doing. Current progress indicates some developments in terms of plugin loading but at this point who knows anymore.Do not delete the vkDevice I beg"}, {"post_id": 58593, "ts": "2026-08-31T02:03:34Z", "hours": 139.4519, "text": "Out with the old, in with the new. A big gripe i had with the engine was it’s long compile times. Thus by compartmentalising it into multiple dll modules, it’s smaller and gets me faster build times! Besides, you can develop your own features using the ABI. Pic unrelated (Inverse kinematics demo)"}]...
|
Title: liteNgine by @AzzyAzimuth | Stardance
Devl Title: liteNgine by @AzzyAzimuth | Stardance
Devlogs:
Out with the old, in with the new. A big gripe i had with the engine was it’s long compile times. Thus by compartmentalising it into multiple dll modules, it’s smaller and gets me faster build times! Besides, you can develop your own features using the ABI. Pic unrelated (Inverse kinematics demo)
I’m cooking up a storm. The storm is leaking memory. Both sentences can be true without detracting from the skill I genuinely do not possess. My copy and paste Keys have been obliterated and my commits have become more and more loose with their terms due to the decreasing amounts of awareness I possess about what the heck I’m doing. Current progress indicates some developments in terms of plugin loading but at this point who knows anymore.Do not delete the vkDevice I beg...
|
1792659273.8753834
|
1793264073.8753834
|
|
32004
|
FW 190 - RC Airplane by @BAUMAU | Stardance
|
BAUMAU
|
0
|
147.44
|
0
|
[]
|
[{"post_id": 79030, "ts": "202 [{"post_id": 79030, "ts": "2026-10-12T12:48:11Z", "hours": 10.0228, "text": "Just like last devlog, I didn’t do too much exciting stuff. I changed the attachment of the nose and inner parts-tray, so the nose is screwed to the main body, while the tray is just tucked in place. I reprinted some parts and cleaned them up. I cut the carbon tube that goes trough the wings and bent the springsteel wires for the front and back gear. The picture just shows all the sketches I drew during the entire design process, which I just think looks cool. See you on the next (and probably last) devlog =D"}, {"post_id": 76385, "ts": "2026-10-03T11:33:38Z", "hours": 9.4903, "text": "Since last devlog, I didn’t do much exciting stuff. I spent hours trying to solder everything, only to find out that the soldering irons tip was the problem all along. I cleaned up, tested tolerances on the printed parts, made small changes to the model and reprinted some parts so they would fit. Not very interesting, but it brings the project forward."}, {"post_id": 73923, "ts": "2026-09-26T10:30:23Z", "hours": 9.7311, "text": "Finally, after so much time, all the parts are there. I still need to solder some things and make sure everything fits nicely, but after that, the final assembly may begin : D"}, {"post_id": 71147, "ts": "2026-09-20T11:00:20Z", "hours": 10.0317, "text": "Hey guys, I am finally printing all the planes parts now! I‘m printing with ASA Aero which was an absolute nightmare before having the right settings, with strings everywhere and many failed prints. But now the settings work and the prints look great for the filament. Probably only one more devlog before shipping this project XD. See you on the next one."}, {"post_id": 68842, "ts": "2026-09-15T20:56:57Z", "hours": 10.7514, "text": "Welcome to devlog number 8. Most I did, was to design some gears for the back steering-mechanism. It now consists of one twirly gear on a small motor I took out of a servo, which slowly turns a chain of gears. I also made some small additions, mainly to stabilise the structural integrity after putting the different printed parts together."}, {"post_id": 66898, "ts": "2026-09-12T21:01:34Z", "hours": 9.3947, "text": "Welcome to my 7th devlog. Since the last devlog, I have worked on fixations for the cockpit, the back wheel, some irl hardware stuff and the steering mechanisms in the back of the plane. As it turns out, there is so little room in the back of the plane, that I could not fit a servo in there to deliver the needed form of motion. Because of that, I had to deconstruct a servo irl, so I can measure and fit the single parts (potentiometer, motor and the pcb) where I want them to be, kinda like a custom servo-version. The cockpit now has little springs in the back and some kind of nose in the front for a quick montage and removal. I also replaced the voltage step-down converter of the little electronics complex for reading and sending the controllers data I use to control the plane, because the old one wasn’t working. Sadly, it seems like the esp32 I was using to read the data, now has a short-circuit and completely overheats after a short time. Next I will integrate the core servo parts of the servo I demolished into Fusion 360 and hopefully make it work. And buy a new esp32 :( See you on the next devlog :)"}, {"post_id": 65071, "ts": "2026-09-09T22:40:40Z", "hours": 9.4864, "text": "I’m finally back to working on my project! The last days, I mostly worked on the internal arrangement of all the necessary parts like pcb, battery, servos etc. The mechanism for controlling the aileron/flaps now consists of two gears on top of each other at a 90° angle, so the servos wouldn’t have to be in the middle of the plane, creating a space problem. I put the battery, pcb and ESC controller onto a removable tray, so taking the battery out is easier. The tray will be fixed with screws in the back, and be connected to the removable front part of the plane for a semi-easy removal while also holding in place while flying. I also fixed a real-world issue with the gear, where in CAD it would fit perfectly, while being out of place irl. Now the only ramaining thing to do is to design the mechanism to steer the rudder, which is quite tricky, because there is pretty much no space at all to attach anything in the very back of the plane. See you on the next devlog :)"}, {"post_id": 42555, "ts": "2026-08-07T11:38:38Z", "hours": 9.3664, "text": "Hello guys, I am finally finished with the pcb design. Now all that is left to do is to finish the steering mechanism in the plane, adding a mount for the pcb and then assembling everything. Sadly I wont be able to work on this project for about a month or so, since im on vacation. See you on the next one!"}, {"post_id": 40446, "ts": "2026-08-03T23:27:21Z", "hours": 9.6944, "text": "Hey guys, this devlog I just fixed some problems with the 3D model of the aircraft and added a screw mount for the cockpit, so it holds onto the main body and is detachable. I also started to do some research on electronics and pcb design for a simple main pcb in the plane. I even started with the schematic but still have quite a lot to learn and do."}, {"post_id": 38458, "ts": "2026-07-31T20:45:20Z", "hours": 46.2483, "text": "Hi guys, it has been quite some time since my last devlog in which I promised to show the plane model. Now I am almost finished with it. Because this was my first time modeling something as complex and dynamic, it took me much time to figure things out. I also did the first test 3D print which pointed out some important issues I will need to fix. Heres a quick overview of how I intend the aircraft to function: -The main body will consist of multiple parts which get put together with glue -The wings will further be stabilised by a carbon pipe -It will have retractable gear -The flaps will be controlled by spinning rods connected to gears inside the main body -Everything will be connected to a main pcb and controlled by an esp32 which receives the data via an antenna See you on the next devlog!"}, {"post_id": 27809, "ts": "2026-07-13T18:10:01Z", "hours": 8.4608, "text": "Hello guys, this is my second devlog on my RC airplane. I’m finally finished with programming both the receiving and transmitting ESP32 boards which I use for this project. Heres a list of all the things I did/added and fixed since the last devlog: -I got the antennas working -The radio connection works and transmits the data I want -The receiving ESP32 now hands the signals down to servos -Fixed a problem with the servo library where attaching the servos to any pin would overwrite other attached servos -Added an easy kind of failsafe -Built a container for the electronics that goes on the back of my controller on the transmitting end–> still working on it but almost finished I will update you guys, when I have a decent 3D model of the plane to show."}, {"post_id": 25337, "ts": "2026-07-09T15:50:52Z", "hours": 4.7606, "text": "This is my first ever devlog so don‘t expect too much. Heres what I already did: -I set up my coding workspace -I connected the ESP32 to my controller and can read the data -I set up the Antennas and tested them Now I just need the Antennas to work together with the rest. After that I will start to design the plane in CAD. Thats it for the devlog, I‘ll post the next one, when i‘m finished with the main coding part."}]...
|
Title: FW 190 - RC Airplane by @BAUMAU | Stardance Title: FW 190 - RC Airplane by @BAUMAU | Stardance
Devlogs:
This is my first ever devlog so don‘t expect too much. Heres what I already did: -I set up my coding workspace -I connected the ESP32 to my controller and can read the data -I set up the Antennas and tested them Now I just need the Antennas to work together with the rest. After that I will start to design the plane in CAD. Thats it for the devlog, I‘ll post the next one, when i‘m finished with the main coding part.
Hello guys, this is my second devlog on my RC airplane. I’m finally finished with programming both the receiving and transmitting ESP32 boards which I use for this project. Heres a list of all the things I did/added and fixed since the last devlog: -I got the antennas working -The radio connection works and transmits the data I want -The receiving ESP32 now hands the signals down to servos -Fixed a problem with the servo library where attaching the servos to any pin would overwrite other attached servos -Added an easy kind of failsafe -Built a container for the electronics that goes on the back of my controller on the transmitting end–> still working on it but almost finished I will update you guys, when I have a decent 3D model of the plane to show.
Hi guys, it has been quite some time since my last devlog in which I promised to show the plane model. Now I am almost finished with it. Because this was my first time modeling something as complex and dynamic, it took me much time to figure things out. I also did the first test 3D print which pointed out some important issues I will need to fix. Heres a quick overview of how I intend the aircraft to function: -The main body will consist of multiple parts which get put together with glue -The wings will further be stabilised by a carbon pipe -It will have retractable gear -The flaps will be controlled by spinning rods connected to gears inside the main body -Everything will be connected to a main pcb and controlled by an esp32 which receives the data via an antenna See you on the next devlog!
Hey guys, this devlog I just fixed some problems with the 3D model of the aircraft and added a screw mount for the cockpit, so it holds onto the main body and is detachable. I also started to do some research on electronics and pcb design for a simple main pcb in the plane. I even started with the schematic but still have quite a lot to learn and do.
Hello guys, I am finally finished with the pcb design. Now all that is left to do is to finish the steering mechanism in the plane, adding a mount for the pcb and then assembling everything. Sadly I wont be able to work on this project for about a month or so, since im on vacation. See you on the next one!
I’m finally back to working on my project! The last days, I mostly worked on the internal arrangement of all the necessary parts like pcb, battery, servos etc. The mechanism for controlling the aileron/flaps now consists of two gears on top of each other at a 90° angle, so the servos wouldn’t have to be in the middle of the plane, creating a space problem. I put the battery, pcb and ESC controller onto a removable tray, so taking the battery out is easier. The tray will be fixed with screws in the back, and be connected to the removable front part of the plane for a semi-easy removal while also holding in place while flying. I also fixed a real-world issue with the gear, where in CAD it would fit perfectly, while being out of place irl. Now the only ramaining thing to do is to design the mechanism to steer the rudder, which is quite tricky, because there is pretty much no space at all to attach anything in the very back of the plane. See you on the next devlog :)
Welcome to my 7th devlog. Since the last devlog, I have worked on fixations for the cockpit, the back wheel, some irl hardware stuff and the steering mechanisms in the back of the plane. As it turns out, there is so little room in the back of the plane, that I could not fit a servo in there to deliver the needed form of motion. Because of that, I had to deconstruct a servo irl, so I can measure and fit the single parts (potentiometer, motor and the pcb) where I want them to be, kinda like a custom servo-version. The cockpit now has little springs in the back and some kind of nose in the front for a quick montage and removal. I also replaced the voltage step-down converter of the little electronics complex for reading and sending the controllers data I use to control the plane, because the old one wasn’t working. Sadly, it seems like the esp32 I was using to read the data, now has a short-circuit and completely overheats after a short time. Next I will integrate the core servo parts of the servo I demolished into Fusion 360 and hopefully make it work. And buy a new esp32 :( See you on the next devlog :)
Welcome to devlog number 8. Most I did, was to design some gears for the back steering-mechanism. It now consists of one twirly gear on a small motor I took out of a servo, which slowly turns a chain of gears. I also made some small additions, mainly to stabilise the structural integrity after putting the different printed parts together.
Hey guys, I am finally printing all the planes parts now! I‘m printing with ASA Aero which was an absolute nightmare before having the right settings, with strings everywhere and many failed prints. But now the settings work and the prints look great for the filament. Probably only one more devlog before shipping this project XD. See you on the next one.
Finally, after so much time, all the parts are there. I still need to solder some things and make sure everything fits nicely, but after that, the final assembly may begin : D
Since last devlog, I didn’t do much exciting stuff. I spent hours trying to solder everything, only to find out that the soldering irons tip was the problem all along. I cleaned up, tested tolerances on the printed parts, made small changes to the model and reprinted some parts so they would fit. Not very interesting, but it brings the project forward.
Just like last devlog, I didn’t do too much exciting stuff. I changed the attachment of the nose and inner parts-tray, so the nose is screwed to the main body, while the tray is just tucked in place. I reprinted some parts and cleaned them up. I cut the carbon tube that goes trough the wings and bent the springsteel wires for the front and back gear. The picture just shows all the sketches I drew during the entire design process, which I just think looks cool. See you on the next (and probably last) devlog =D...
|
1792518897.5662298
|
1793123697.5662298
|
|
8298
|
Sephyr Magic mod for Minecraft by @Szczurek | Star Sephyr Magic mod for Minecraft by @Szczurek | Stardance...
|
Szczurek
|
0
|
147.82
|
0
|
[]
|
[{"post_id": 81656, "ts": "202 [{"post_id": 81656, "ts": "2026-10-21T22:33:18Z", "hours": 9.8467, "text": "Multiloader modding is pain Can’t NeoForge and Fabric just have one common api, for all the boring stuff like registration, networking and events? I’ve had enough, so I spent 2 hours porting my mod to Balm , which is a library providing exactly that. The result is beautiful - ~150 lines are gone, and the loader specific subprojects fit under 10 lines of code, as I was able to move everything to common. Now for the worst irony: I spent my time doing all of that and the next thing I’m gonna do is… disable fabric support 😭 Why, you may ask? I was forced into it by reality read on First VFX I made my first VFX, for the leap spell. You can see it in the attached video. It’s going to spawn under player’s legs. For now it’s a really simple one, as I’m rushing to add a bit more of content, than just a single spell, before Stardance ends. It is made using Photon Editor , which is a VFX editor for Minecraft. It has a lot of good stuff, like emitters, timeline and animation curves, which will be crucial in making more advanced effects. Only downside: It doesn’t support Fabric, which is why support for it is going to be disabled for now, until I manage to hopefully make a port it in the future. Reworking spell internals (again) After making the effect, I realized I have no way to run it from the spell code, because spell logic runs server side only, and VFX needs to be played on the client. So I wen’t back to the drawing board, and I’m going to change how spells run. They are going be entities now, but more on that next devlog, because it’s not done yet, as I ran into friction in the code, which triggered me to do the Balm refactor, and in the meantime the time counter hit 9,5h. Do NOT hardcode stuff please I had to do some work on my laptop today, which has NixOS. So I update my Sephyr clone, try to run it, and bada bing bada bum, it doesn’t work! What’s going on? After a hour of debugging, it turned out that NeoForge devs hardcoded their Gradle plugin to run one specific task using Java 21. The rest runs on Java 25 (the one used by Minecraft 26.1). So, be default, a NeoForge setup for Mc 26.1+ requires two java version. Normally the second one is autodownloaded, but that doesn’t work on NixOS, and that’s also bloat. I don’t want that! So I made a bug report . Fortunetely it isn’t really hardcoded, just dumb, and I was able to fix it with this line in build.gradle Update to Burn 0.22 Burn 0.22 was recently released, removing the Backend generic from most of it’s traits. I updated Sepple to work with that. I also had to regenerate the model code for the multipa model. I was hoping the update would provide some speedup to speech processing, but I wasn’t able to measure any difference. Replacing Multipa with Zipa I found a new speech-to-IPA model. It’s called Zipa , and it looks very promising. It’s around 4x smaller then multipa, which means faster processing and less to download for end users. Furthermore, according to it’s paper, and my few tests it has noticeable better accuracy! So I implemnted it. It was a little more trouble then multipa, because the model has a much richer pre-processing. The audio waveform is turned into a mel spectogram, Fourier Transform is done, and other stuff happens. Buuut there is a problem. It turns out the model is great at doing recognition on longer audio sequences, and doesn’t really work that well on short ones. For them it performed worse then multipa! That means it’s not suitable for doing recognition in realtime, at least when plugged in the same way multipa was. However I have a couple ideas to try, which might, hopefully, solve the issue. Until I do that, it is set aside on a separate branch."}, {"post_id": 79314, "ts": "2026-10-13T14:21:33Z", "hours": 10.1344, "text": "Debugging gui is (almost) done What is going on, why already 10 hours passed? This was menat to be a quick sidequest, where I built this gui tool for debugging how well detecting magic keywords is working BUUT given it is the third devlog about it That means it took over 30 hours! Well, happens. So, all of this devlog was spent building all the plumbing needed to capture all the data from the detection pipeline and sending it in realtime to the app. All of it is using done using channels. They are injected into the detection pipeline either as Tap<T> ‘s - a new pipeline element that clones data it sees, sends to channel, and passes it on, or using wrapper types around existing processors. The app then receives the data and displays in on the plot. I also encountered and fixed few bugs, that I didn’t expect. For example originally the zoom went to crazy values, because a timestamp used to determine where the data ends wasn’t converted to be relative to the timeline start. I also found an quite an issue introduced 3 months ago, where the timestamps attached to audio weren’t calculated properly, making entire audio chunks have identical stamps. It was minor enough that I never noticed till now, when the data landed on the plot and resulted in this funny thing you can see on the attached screenshot. Finally I added a simple scrolling animation, to amortize the jitter caused by bigger chunks of audio coming, so the plot doesn’t jump that much when going live, but it’s still not as smooth as I would like it to be. As mentioned before I was using AI assistance in strictly controlled manner (I carefully planned, revieved, cleaned up and adjusted to my liking all the code generated by it) to build this debugging tool faster, and by now I’m very tired of it. At the end of the work it started doing stupid things (possibly because some context was lost?), which made me question if it was making me faster or slower. Luckily the work is mostly over, and I can finally get to other, more interesting stuff, that will be fun to do, with no AI. Next I want to use the tool to improve the detection a bit, and spend more time working on the actual mod."}, {"post_id": 77831, "ts": "2026-10-08T13:13:43Z", "hours": 10.2597, "text": "Keyword detection UI is done Look as this! It’s so pretty! Seeing all the data visualised is quite mesmerizing. I know the image will probably attract more views, so lemme explain what you are looking at. I’m making a minecraft mod where you cast spells by saying custom made up words, and this is a tool for debugging the keyword detection system. It’s meant for me, to help me make the detection more accurate, and reliable. I’ve been building it for the last ~20 hours, and in the last two I built the rendering of all the 8 strips. Check previous devlog if you want to know what exactly they display. It was pretty smooth sailing. As said before, since this is just a debugging tool for me, that I want to have finished quickly for it I’m using AI assistence, by letting it do stuff in small, pre-planned steps, and unsloppifying all the code between them. It’s working well. What helps, is that entire app is built on the egui / eframe crates, which I used before, and are somewhat familiar with. I gotta give credit, where is due, their api is really intuitive for the most part, and simple to use. The entire chart is also zoomable, and scrollable, and making this work was a bit of pain. I wanted to limit the accesible range to only around the data (so you can’t scroll away to far from it), and egui_plot doesn’t really provide a way to do it, so some manual calculations had to be done. I wanted to have some breathing space by allowing 0.25x of the zoom after last audio sample to be visible. This was the worst part, as it’s kind of a recursive problem - the zoom value affects how much zoom is allowed. But after around two hours I manged to figure it out, but keeping the original zoom value independantly of the limit calculated from it. Worth noting - what you see is just a mockup data, and the gui isn’t actually hooked up to the detection system, as that’s what I’m going to do next. After that I can hopefully improve the detection, and the finally go back to minecraft coding."}, {"post_id": 76510, "ts": "2026-10-03T19:41:08Z", "hours": 11.5369, "text": "Visualizing the keyword detection pipeline I’m not happy with the accuracy of the magic words detection system. It work half of the time, sometimes better, sometimes worse depending on the word. This make casting longer spells quite troublesome. Debugging why a specific word wasn’t detected is hard, debug prints can only get you so far. So I decided to go on a crazy sidequest and build a gui tool, that will help me debug this. I spent over 2 hours designing the concept, and over 10 implementing it, and it’s around 40% ready. The tool will display 8 strips / plots, and each plot will display some data, all on a shared timeline on the bottom, that will update live as the audio is processed. Strips 1, 2, and 4 will display waveforms of the audio at different processing stages. Strip 3 will show Voice Activity Detection score (which is used to filter out non-speech). Strips 5, 6 and 7 will display detected IPA tokens, accumulated text buffer, and detected words respectively. Strip 8 will display latency stats, which will help in optimizing it. This way I will be able to easily see, “oh, this phoneme wasn’t detected” or “oh, VAD cut out this word” or “oh, some mess accumulated in the buffer” and tweak my algorithms. AI - it’s bad, or is it? Despite looking quite simple on the surface, the app is still quite an undertaking. And it’s just a debugging tool! A thing to help me get the main goal done! I don’t want to spend too much time on it. So I considered using AI as a tool ( NOT vibecoding ). I hate vibecod(ing, ers), and I’ve always steered off AI, because very often I wasn’t satisfied with the output quality. I take pride in writing good code. But recently I’ve been trying out OpenCode here and there (on free models), and was quite surprised with it’s capabilities. Sure, it’s still often slop, but a one you can quite quickly correct, and sometimes be more effective if used correctly. AI - is it helpful? So I decided to do an experiment - can carefully planned AI usage help me write the debugging tool quickly? I started off by designing the functionality of the tool - I described the idea in detail to MiMo-V2.6-Flash, and asked it to interrogate me about as many details as possible and needed. I had to answer 35 questions, some of which were quite observant, picking me on nooks and crannies. After that a markdown file describing the design was written, and then went through few corrections. Next up, in a new session I asked MiMo to create a bullet point outline of steps needed to be done to implement the tool specified in the design file. Then one by one I went over each bullet point, asking the just released LongCat 2.5 (which has a bigger ctx window) to ask me questions, and formulate an implementation plan of it, present it to me, I gave corrections, and the point in file was replaced. This way I ended up with a second markdown file - an implementation plan. This took way too long, and was really boring. So, finally coding time. I do it like this: Ask opencode to implement a step of the plan Review the diff in vscode Remove fluff and slop manually, make sure all new code is good quality. Give feedback if I something doesn’t work like expected, or also fix it manually. After it’s good - commit Repeat for the next step It’s still not super fun, but at least I’m touching the code now, so much better already. And it is efficient! I believe it definitely would take me longer, if I wasn’t using AI. So the morale is: If you want fun, or doing something important - do it manually. If you want to do something quickly and get it out of the way less important. Here is a screen of how it is looking now: the general shape is in place, but there is no plots yet."}, {"post_id": 72706, "ts": "2026-09-23T15:27:59Z", "hours": 6.3589, "text": "The Stack Overflow For some reason my mod broke and started crashing during startup, and it did so in the worst possible way - without any error message! All I got in the Intellij Idea, was a message that the process exited with the signal code 11. WHAT? That signal corresponds to SIGSEGV signal on Linux, which generally means you did something wrong with the memory. But this is Rust! There shouldn’t be any memory issues! I started searching, and I learned there is a tool called coredumpctl, which lets me access a special dump file generated in such cases, and analyze it in a debugger like GDB. So I did that, and what I found was even more bizzare! The debugger was pointing to a start of a function, before the first line. I added some debug prints, and to my disbelief, the print before calling that function ran, and the the second one, which was the first line inside the function didn’t! Fast forward a bit I managed to figure out the problem, with some help from a friend. It turned out I encountered an stack overflow! When the function was called, Rust tried to expand the stack, and crashed because the stack ran out! Why? Because the code was loaded in Java, and Java by default allocates less memory for the stack ! The solution was manually requesting a bigger stack size during thread creation in Java. I also discovered, why the problem appeared - in the Rust file download code I had allocated an array buffer of 1MB. It landed on the stack, and that was too much! The solution was using a Vec, which lives on the heap instead. Stupid problem, but took 3 hours of my time 😩 Mod init progress reporting Sephyr requires the IPA speech recognition model to work, which needs to be downloaded on the first run, and subsequently loaded from the disk. The logic was overhauled providing more corectness, and hooked up to a series of toasts. File download progress bar is shown, and the intialization state (or failure) is coummunicated to the user! See it in the video"}, {"post_id": 71471, "ts": "2026-09-20T21:50:14Z", "hours": 9.7653, "text": "Wind spells have sound now Using LMMS and ZynAddSubFx I made two wind sound effects, which now play when casting a wind spell. You can hear them in the attached video. Cross-Compiling is fun The speech recognition system is written in Rust, which compiles down to native code. This means that it needs to be compiled separately for each operating system, and processor architecture. Rust is more friendly to it, then C or C++ for example, but as always, compilation finds it own weird ways to break. I took the setup used by Sable as a base, with two Docker containers, one for Linux and Mac, and one for Windows. BUT: The Rust version installed in the docker image was not new enough for one of my dependencies - I had to update the rust toolchain in the container image. Compiling for linux required ALSA headers for audio access - I had to figure out, how to install them in a way, that works for both x86_64 and arm64, without breaking each other. The latest version of the base image has Mac cross compilation broken - I had to use a previous one. The image ships a MacOS SDK from 5 years ago - too old, doesn’t work, so it also had to be replaced. - the container is kinda becoming a Ship of Theseus xD My code was pulling openssl as a dependency, which was failing to compile - I had to spent over an hour, figuring out how to get rid of it from the dependency tree, discovering I can do so by updating a dependency to an unreleased version. Gradle obviously required some tweaks, who doesn’t love Gradle right? In the end I managed to get it working, which brings the mod closer to being usable by the public, outside dev environment. Downloading the model The mod works by running an IPA speech recognition model locally. The model weights around 1.2G, making it too large to be embedded in the mod jar. So I added automatic download support - the model can now be downloaded automatically on the first time an user runs the mod. There is even progress tracking, though it’s not hooked up to Minecraft yet. A Toast is also shown when the loading fails, making the mod more user friendly."}, {"post_id": 69097, "ts": "2026-09-16T11:23:50Z", "hours": 10.2308, "text": "SPELLCASTING WORKS! The main mechanic of the mod is working! YOU CAN CAST SPELLS WITH VOICE! Finally! It took soo long… After over two months of development, I finally have a proof, that my crazy idea actually works, and was worth trying out. As you can see (and hear) in the video I implemented a simple wind leap spell, as the first one in the mod. Note: There are no visuals or sounds yet, but they will be added, and this is just a proof of concept. The spell comes in three variants ʔalɪˈvɑn vɛɾtɛɾ ɛvi ˈonːdˌoː - wind launches you forward ʔalɪˈvɑn vɛɾtɛɾ ɛvi ãntikɔ - wind launches you backward ʔalɪˈvɑn vɛɾtɛɾ ɛvi fɒksɑm - wind launches you up (no matter where you are looking), not shown in the video Additionaly, it can be cast as just ʔalɪˈvɑn vɛɾtɛɾ ɛvi , which is a shortcut for the first variant. This can seen in the third attempt, where the system didin’t catch onːdˌoː , but the spell worked anyway. This flexibility is easily possible, thanks to Mojang’s brigadier library. Brigadier powers the / commands in vanilla, and now the spell syntax and execution in my mod. I settled on it after trying two alternative approaches and failing to make them work. After all, my spells are just sentences, with the only difference, is that you speak them instead of writing."}, {"post_id": 55219, "ts": "2026-08-26T20:03:24Z", "hours": 10.6233, "text": "Note: This one is kinda boring, mostly plumbing stuff to get things to work, feel free to skip it. Sephyr (the mod) I’m working on setting up the the logic for registering and casting spells. This is quite hard, as the design of the mod is far from complete, which means I don’t even know what exactly do I need. I created a registry of elements, which are a source of a color, and are going to have other effects in the future. The first word of a spell is its element. For example in the spell ʔalɪˈvɑn vɛɾtɛɾ ɛvi the word ʔalɪˈvɑn means wind. I also made a basic system for figuring out the spell the player is casting. For now its very simple and can’t handle more complex spells, for example, with optional words. The entirety is a big mess as I’m figuring things out, and the fact I’m making this mod for both Neo and Fabric doesn’t make it any easier. I also updated the mod to Minecraft 26.1.2, which means there is going to be no 1.21.1 release. Sepple (word detection system) I keep working on collecting data to finetine the speech to ipa ml model. After discovering a better way of getting the audio files while writing last devlog I scraped the download script, and instead replaced it with a new one to extract needed files from the 20GB Wiktinary audio dump tar. I also cleaned up the entire code, turning it back to human standards, after a bit of Opencode usage. (Since those are one-time scripts to extract data I decided using a bit of AI in this area won’t hurt. I don’t use AI for the rest of the project.). I also prepared a fourth script, that will use the existing multipa model, to generate IPA transcription of the files. Then files with generated IPA being too different from original IPA will be discarded, as a way to detect files, where the IPA doesn’t match the audio (because the audio contains more text for example)."}, {"post_id": 45897, "ts": "2026-08-12T17:21:05Z", "hours": 10.2356, "text": "Sephyr (the mod) A proper lifecycle of the detection system was added. The IPA model is loaded in background during game start, but the audio capture and processing start waits until you join a world, and stops once you leave it. The Rust code now has a logging hook, so that it can do civilized logging using the Java logger, instead of blatantly writing to stdout. Sepple (the voice casting detection system) I made the pipeline system cancellable, so that the mod can cleanly stop the detection, which was not possible before. Support for running the speech detection inference on GPU was added as a feature flag. For now it is not used by default, as it showed no measurable improvements over running on CPU on my PC, but it might be useful on other, weaker devices. Collecting data for training As I said too many times, the speech to IPA model used for magic speech detection is not quite the quality I would like it to be. I’m hoping to improve it by fine-tuning it on its weak areas, but for that I need some data. My idea is to use wiktionary as a data source. It is a free online dictionary run by the same people who run Wikipedia. Most words on it have the pronunciation attached both as an IPA and an audio file, which makes it ideal for the task. I downloaded a preprocessed jsonl dump file from kaikki and wrote a script to extract just words and ipa from it. Unfortunetely it was missing urls of the audio files. So I had to download a raw xml dump of the entire english wiktionary, and spend few hours writing a script to extract them. Then I used deepseek a generate a third script, to download all the audio files from a list. There is going to be almost 20k of them, and the script is running right now as I’m writing this, and it is going to take multiple hours as I can only download around 1 file per second to respect rate limits and not overburden wikimedia. And just now while writing I realized I’m an idiot, because kaikki provides a 20GB tar file with all the audio, downloading which would be more efficient."}, {"post_id": 41944, "ts": "2026-08-06T14:02:27Z", "hours": 9.6106, "text": "Integrating Rust IPA word detection with Minecraft With the IPA based magic keyword detection (which is written in Rust) working I spent all of this devlog integrating it into Minecraft. I created a new repo for the mod, using the Jared’s template. I decided to go with supporting both Fabric and Neoforge, and picked the 1.21.1 Minecraft version, though in the future I’m planning to move to 26.2 or later. I set up an additional gradle subproject and a rust package, that both together act as a Java to Rust bridge, using the j4rs crate. Using it I managed to initialize Sepple (the detection system), and get it running. The tricky part was going the other direction, to pass detected words from Rust to Java. For some reason Rust wasn’t detecting the JVM and was trying to spawn a new one! After a bit of experimentation I concluded, that I can only call Java from Rust only during the period when Java calls Rust, and only on the same thread. In the end I managed to get it working, by spawning a new thread on the Java side, and calling a blocking Rust function, that kept calling a Java callback passed to it. Originally I tried to use a static function as a callback, but for some reason (probably some kind of classloading isolation) the system was failing with some crazy null errors. I also had to adjust Sepple a bit, to not require a bunch of random json files at specific paths, and to provide an mpsc chanell directly outside its pipeline, for less boilerplate. With that I have a proof of concept working! Minecraft is receiving the detected words and printing them to chat. Note that those are all custom words, created by me specifically for this mod. This is why I had to create an entire new system for detecting them, and why they are written in the International Phonetic Language, which is also why they look a little weird in the minecraft’s font. The Java<->Rust setup is also not very good yet. For example the packaging of compiled Rust is very basic, and you can’t control the word detection from Java - no way to stop it or restart. There is also no good error handling, which means if a file is missing it will just crash everything. But it works, and I can continue working on the mod, and making it better!"}, {"post_id": 36043, "ts": "2026-07-27T21:21:56Z", "hours": 10.1719, "text": "About project Sephyr is a magic mod, with spell casting powered by speech live translated into International Phonetic Alphabet, to detecting custom-made spell words in it. Adding Voice Activity Detection - part 2 After the refactor I was able to finally add the VAD. But after few tests I noticed that the VAD was making things worse, not better! That was as a part of the previous devlog (which ran out of character limit). I added a pipeline part that writes every chunk of audio that passes through it to a file, so it can be inspected. With it I found the main problem: It took few hundred milliseconds before the VAD detected speech, so a small chunk was always cut off from the start. To fix this I added context keeping: A bunch of previous chunks are kept, and when speech is detected they are also taken into consideration. I also made the sliding window chunking aware of VAD - the VAD detector sends a signal when it detects an end of speech. The chunker then flushes its buffer into the pipeline, so that it doesn’t get stuck until next speech, and replaces it with precise amount of silenece. This way the start of next audio doesn’t get cut off later due to the sliding window mechanism. The author of the bunsen crate (providing the VAD model implementation) also released version 0.29.0, which fixed all the issues I encountered in the last devlog, and updated its API slighly, which meant that after adjusting my code I could stop using my own fork of it. Improving word detection from IPA My friend found a crate named phonetics-rs and suggested using it to improve the algorithm. This crate calculates the difference between two IPA strings, taking into account how similar they sound. This is much more accurate than just comparing strings on grapheme level. I modified my algorithm to use it, and it turned out to be a great idea, that significantly improved the detection rate of magic words. I also changed the dictionary to be sorted by longest words first, so that they take priority over short ones. So far, testing and improving the word detection algorithm has been the most fun out of the entire project. Looking at IPA strings, and figuring out why some speech has or hasn’t been classified as some word is quite fascinating. For example, I encountered a string pɾilifɛra which the model output after me saying prizim and fɛra , the two magic words that I made up for the spell system. Surprisingly, my algorithm said the string contained just prizim despite there being a literal fɛra in it. As I found out, pɾilif is closer then pɾili to prizim , and it fell under the threshold, so the algorithm took that part and left out ɛra in the buffer which was too far from fɛra . After seeing this, I made the algorithm scan the string for exact matches first, and then do fuzzy phonetic matching, also ensuring the order is preserved. I also tuned the thresholds, adding more and more tests (there is now 8 of them) to ensure the algorithm behaves well. Finally, I noticed the IPA output from the model changes based on audio’s offset in time. So I wrote a script that lets me test this, and see the effect of different offsets. What’s next There is a saying in data science: garbage in - garbage out. This is also true for my project. The detection works decently, but it also quite often outputs false positives during normal speech. I believe I did I all could to compensate for the unsatisfying quality of the speech to IPA translation model. Now I will be shifting to the Minecraft part of the project - creating a mod, integrating the detection system into it and creating spells. In paralell, I will be exploring the possibility of finetuning the model, to improve the accuracy of it and hopefully get better results. Watch the video with audio, that’s the part that matters right? :)"}, {"post_id": 27916, "ts": "2026-07-13T20:52:15Z", "hours": 30.4097, "text": "Finding words in long IPA strings For the magic system I need to find individual words in the ever accumulating string of IPA characters. To do that I hold a list of valid words in memory. When there is some text, I iterate over the dictionary, calculating a levenshtein distance between each word and start of the string. Then the word with the lowest score is taken and if the score is under a set threshold, the word is considered a match, and the matching part is remved from the buffer. To cover more cases different lengths of the string are checked. If no match is found, one character is removed from the string, and the process repeats, until all words are found. Fun fact: When comparing IPA and other non-ascii strings you need to make sure you are considering graphemes and not characters, because in UTF-8 what we perceive as one symbol, can be encoded as many characters in memory. So far this algorithm works quite well, though in the future it might need some performance optimization, or tuning, if it starts yelding too many false positives. Other misc changes: Migrated main fn to use clap for parsing arguments Added flake.nix with dev env so I can run this code on my NixOS laptop Added time information to the pipeline - Together with the audio from the mic, a timestamp is captured, and passed through entire pipeline up to the end, where it is used to discard text older then 200ms from the buffer, so that stale data doesn’t mess up new words. Adding VAD I noticed that the speech to IPA model likes to output random data when no one is talking, so I decided to add VAD - Voice Activity Detection - a system that detects if someone is talking, and only then passes the audio on. I estimated this to take me around 2 hours, and oh, how wrong I was? It took 10x that, which is why this devlog documents 3x the normal amount of time. And it still isn’t fully ready! So let me tell you what I did and why it took so long. In the Burn (ML/AI lib for Rust) discord I noticed a solution just dropped out of the sky: Merely few days before a Silero VAD model implementation was added to a library named bunsen . ML models take input as Tensor s - multidimensional arrays, which do not say in what form or shape the data is expected. So I had to figure that out myself. Fortunetely it was just 512 elements long chunks of 16khz audio with no additional pre-processing. Knowing that I fed the microphone input to the model and began to watch it output complete nonsense unrelated to whether I was speaking or not. So I started debugging - checked my code, wrote an equivalent in python using the official silero implementation to compare results - completely different. So I cloned the bunsen library, wrote an unit test there and ran off a Opencode agent. I also contacted the library author. He found one major issue, and the agent identified a second problem and wrote a solutuion - Silero VAD expects 64 samples of the previous chunk to be passed along the 512 to work correctly. I cleaned up the code, which was quite a pain because rust-analyzer was super slow on that repository, and made a PR, but it wasn’t merged because the author went with his own way of implementing it. The big refactor Coming back to my project I realized that my code strucutre makes it very hard to integrate VAD into it. So I spent the next 6 hours of so refactoring almost every file in the codebase into a new pipeline system. It lets you compose a pipeline out of a producer, many processors, and a consumer. It makes use of async, which saves on the amount of threads used. Static type checking ensures you can’t compose parts that wouldn’t work together, and all elements can also define contracts on the size of data chunks they produce and consume. I ran out of the character limit, so the rest of VAD story will be in next devlog."}, {"post_id": 11428, "ts": "2026-06-18T18:11:53Z", "hours": 4.8753, "text": "Live speech to IPA transcription in Rust using ML I finished my rough implementation of a live speech transcription pipeline. It captures my speech from the mic, and processes it with the Multipa model using the Burn framework. To make it work live I implemented a sliding window that chunks the audio into 2 second fragments, each of which is processed separately, and then assembled into a single string. The chunks overlap, and are then sliced, so that audio that was on the borders of a cut (which will reduce the quality) is thrown away, and the text for it is taken from the next chunk, for which it is at the center. The inference is running on cpu for simplicity and compatiblity, so by sacraficing a bit of pain I added threading (where each chunk is processed on a separate thread) to make sure the pipeline can keep up. I’m slightly worried about how the perfromance will be when this is going to run along Minecraft, but hopefully I will be able to make it as lightwieght and unintruisive as possible. Finally I reworked how the text is accumulated, switching from a vector of logits to a string. Since IPA has no concept of words, just phonemes, everything I say is appened to one long string. So I added a very simple silence detection, where if a captured chunk returns no text a space is added to the string. For easier teasting I also improved the main function, so that the program can: take a file and process it all at once take a file and process it using the pipeline run live mic capture on the pipeline What’s next - improving the detection In theory this is all I need, and I can start working on the Minecraft mod. However I’m not satisfied with the quality of the transcription, and at the level it currently is most spell detection will probably fail. The quality of the model itself is pretty bad. If the description on github of the Multipa model is correct, the model was trained on merely 9 hours of speech data, which probably explains it. The model is also sensitive to noise, and distractions like other people talking, and sometimes outputs garbage. So the next devlog I will be experimenting with various techinques of filtering and cleaning up the audio the model is receiving, and potentially even training a new model myself on a bigger dataset. Attached video Working demo of live capture and transcription of me talking."}, {"post_id": 9209, "ts": "2026-06-15T13:48:33Z", "hours": 3.7658, "text": "Sephyr - project overview Sephyr is an unique magic mod for Minecraft. It aims to differentiate itself by: Introducing a voice powered spell casting system , that’s both fun to use and intricate. You will be reading spells out loud into your mic in a magic language, and what will you say will affect the result of the spell. Having stunning, detailed visuals unique to each spell. This is going to be achieved by spending way too much effort on rendering. I want spell casting to be something satisfying, and the look is a key part of it. The project consits of two parts: Sephyr - The minecraft mod itself containing the spells, mechanics and visuals, written in kotlin. Sepple - Speech processing engine, responsible for transforming your babble into something Sephyr can work with and figure out what have you just said. Sepple overview Sepple is a foundation of the voice powered spell casting. It is an audio processing library written in rust, that Sephyr will depend on and embed. It captures your mic live and pre-processes the speech. Then it uses the Burn framework to locally and quickly run a speech recognition model called Multipa . The model transcribes speech into the International Phonetic Alphabet (IPA), a textual notation of various sounds that make up the spoken language. Finally it tries to match the result against a custom dictionary of magic words, which will form sentences - the spells you will be casting. This devlog Before Stardance launched I have written a rough skeleton for Sepple. With some trouble I managed to import the Multipa model to Burn via ONNX export from Pytorch, and implement the required post-processing to decode model’s output into IPA. I also started working on a pipeline for mic capture and sliding window processing, so that the audio can be transcribed live. After Stardance started I noticed that the same input audio file gives quite different result in Pytorch then in Rust. I spent few hours debugging why that is, and found a missing piece in my implemenation. For the Multipa model to work correctly the samples need to be normalized using a so-called z-score normalization. That means calculating the mean of the input and its standard deviation (a statistics measure), and applying a formula that rescales the data, so that it has a mean of 0 and standard deviation of 1. After implemenating that the model started to work correctly and give the same output for the same input on both sides. Picture: “Hello Stardance. Sepple is working now” translated to IPA using pytorch and the fixed Rust implementation. It is not veeery accurate, but it is good enough."}]...
|
Title: Sephyr Magic mod for Minecraft by @Szczurek Title: Sephyr Magic mod for Minecraft by @Szczurek | Stardance
Devlogs:
Sephyr - project overview Sephyr is an unique magic mod for Minecraft. It aims to differentiate itself by: Introducing a voice powered spell casting system , that’s both fun to use and intricate. You will be reading spells out loud into your mic in a magic language, and what will you say will affect the result of the spell. Having stunning, detailed visuals unique to each spell. This is going to be achieved by spending way too much effort on rendering. I want spell casting to be something satisfying, and the look is a key part of it. The project consits of two parts: Sephyr - The minecraft mod itself containing the spells, mechanics and visuals, written in kotlin. Sepple - Speech processing engine, responsible for transforming your babble into something Sephyr can work with and figure out what have you just said. Sepple overview Sepple is a foundation of the voice powered spell casting. It is an audio processing library written in rust, that Sephyr will depend on and embed. It captures your mic live and pre-processes the speech. Then it uses the Burn framework to locally and quickly run a speech recognition model called Multipa . The model transcribes speech into the International Phonetic Alphabet (IPA), a textual notation of various sounds that make up the spoken language. Finally it tries to match the result against a custom dictionary of magic words, which will form sentences - the spells you will be casting. This devlog Before Stardance launched I have written a rough skeleton for Sepple. With some trouble I managed to import the Multipa model to Burn via ONNX export from Pytorch, and implement the required post-processing to decode model’s output into IPA. I also started working on a pipeline for mic capture and sliding window processing, so that the audio can be transcribed live. After Stardance started I noticed that the same input audio file gives quite different result in Pytorch then in Rust. I spent few hours debugging why that is, and found a missing piece in my implemenation. For the Multipa model to work correctly the samples need to be normalized using a so-called z-score normalization. That means calculating the mean of the input and its standard deviation (a statistics measure), and applying a formula that rescales the data, so that it has a mean of 0 and standard deviation of 1. After implemenating that the model started to work correctly and give the same output for the same input on both sides. Picture: “Hello Stardance. Sepple is working now” translated to IPA using pytorch and the fixed Rust implementation. It is not veeery accurate, but it is good enough.
Live speech to IPA transcription in Rust using ML I finished my rough implementation of a live speech transcription pipeline. It captures my speech from the mic, and processes it with the Multipa model using the Burn framework. To make it work live I implemented a sliding window that chunks the audio into 2 second fragments, each of which is processed separately, and then assembled into a single string. The chunks overlap, and are then sliced, so that audio that was on the borders of a cut (which will reduce the quality) is thrown away, and the text for it is taken from the next chunk, for which it is at the center. The inference is running on cpu for simplicity and compatiblity, so by sacraficing a bit of pain I added threading (where each chunk is processed on a separate thread) to make sure the pipeline can keep up. I’m slightly worried about how the perfromance will be when this is going to run along Minecraft, but hopefully I will be able to make it as lightwieght and unintruisive as possible. Finally I reworked how the text is accumulated, switching from a vector of logits to a string. Since IPA has no concept of words, just phonemes, everything I say is appened to one long string. So I added a very simple silence detection, where if a captured chunk returns no text a space is added to the string. For easier teasting I also improved the main function, so that the program can: take a file and process it all at once take a file and process it using the pipeline run live mic capture on the pipeline What’s next - improving the detection In theory this is all I need, and I can start working on the Minecraft mod. However I’m not satisfied with the quality of the transcription, and at the level it currently is most spell detection will probably fail. The quality of the model itself is pretty bad. If the description on github of the Multipa model is correct, the model was trained on merely 9 hours of speech data, which probably explains it. The model is also sensitive to noise, and distractions like other people talking, and sometimes outputs garbage. So the next devlog I will be experimenting with various techinques of filtering and cleaning up the audio the model is receiving, and potentially even training a new model myself on a bigger dataset. Attached video Working demo of live capture and transcription of me talking.
Finding words in long IPA strings For the magic system I need to find individual words in the ever accumulating string of IPA characters. To do that I hold a list of valid words in memory. When there is some text, I iterate over the dictionary, calculating a levenshtein distance between each word and start of the string. Then the word with the lowest score is taken and if the score is under a set threshold, the word is considered a match, and the matching part is remved from the buffer. To cover more cases different lengths of the string are checked. If no match is found, one character is removed from the string, and the process repeats, until all words are found. Fun fact: When comparing IPA and other non-ascii strings you need to make sure you are considering graphemes and not characters, because in UTF-8 what we perceive as one symbol, can be encoded as many characters in memory. So far this algorithm works quite well, though in the future it might need some performance optimization, or tuning, if it starts yelding too many false positives. Other misc changes: Migrated main fn to use clap for parsing arguments Added flake.nix with dev env so I can run this code on my NixOS laptop Added time information to the pipeline - Together with the audio from the mic, a timestamp is captured, and passed through entire pipeline up to the end, where it is used to discard text older then 200ms from the buffer, so that stale data doesn’t mess up new words. Adding VAD I noticed that the speech to IPA model likes to output random data when no one is talking, so I decided to add VAD - Voice Activity Detection - a system that detects if someone is talking, and only then passes the audio on. I estimated this to take me around 2 hours, and oh, how wrong I was? It took 10x that, which is why this devlog documents 3x the normal amount of time. And it still isn’t fully ready! So let me tell you what I did and why it took so long. In the Burn (ML/AI lib for Rust) discord I noticed a solution just dropped out of the sky: Merely few days before a Silero VAD model implementation was added to a library named bunsen . ML models take input as Tensor s - multidimensional arrays, which do not say in what form or shape the data is expected. So I had to figure that out myself. Fortunetely it was just 512 elements long chunks of 16khz audio with no additional pre-processing. Knowing that I fed the microphone input to the model and began to watch it output complete nonsense unrelated to whether I was speaking or not. So I started debugging - checked my code, wrote an equivalent in python using the official silero implementation to compare results - completely different. So I cloned the bunsen library, wrote an unit test there and ran off a Opencode agent. I also contacted the library author. He found one major issue, and the agent identified a second problem and wrote a solutuion - Silero VAD expects 64 samples of the previous chunk to be passed along the 512 to work correctly. I cleaned up the code, which was quite a pain because rust-analyzer was super slow on that repository, and made a PR, but it wasn’t merged because the author went with his own way of implementing it. The big refactor Coming back to my project I realized that my code strucutre makes it very hard to integrate VAD into it. So I spent the next 6 hours of so refactoring almost every file in the codebase into a new pipeline system. It lets you compose a pipeline out of a producer, many processors, and a consumer. It makes use of async, which saves on the amount of threads used. Static type checking ensures you can’t compose parts that wouldn’t work together, and all elements can also define contracts on the size of data chunks they produce and consume. I ran out of the character limit, so the rest of VAD story will be in next devlog.
About project Sephyr is a magic mod, with spell casting powered by speech live translated into International Phonetic Alphabet, to detecting custom-made spell words in it. Adding Voice Activity Detection - part 2 After the refactor I was able to finally add the VAD. But after few tests I noticed that the VAD was making things worse, not better! That was as a part of the previous devlog (which ran out of character limit). I added a pipeline part that writes every chunk of audio that passes through it to a file, so it can be inspected. With it I found the main problem: It took few hundred milliseconds before the VAD detected speech, so a small chunk was always cut off from the start. To fix this I added context keeping: A bunch of previous chunks are kept, and when speech is detected they are also taken into consideration. I also made the sliding window chunking aware of VAD - the VAD detector sends a signal when it detects an end of speech. The chunker then flushes its buffer into the pipeline, so that it doesn’t get stuck until next speech, and replaces it with precise amount of silenece. This way the start of next audio doesn’t get cut off later due to the sliding window mechanism. The author of the bunsen crate (providing the VAD model implementation) also released version 0.29.0, which fixed all the issues I encountered in the last devlog, and updated its API slighly, which meant that after adjusting my code I could stop using my own fork of it. Improving word detection from IPA My friend found a crate named phonetics-rs and suggested using it to improve the algorithm. This crate calculates the difference between two IPA strings, taking into account how similar they sound. This is much more accurate than just comparing strings on grapheme level. I modified my algorithm to use it, and it turned out to be a great idea, that significantly improved the detection rate of magic words. I also changed the dictionary to be sorted by longest words first, so that they take priority over short ones. So far, testing and improving the word detection algorithm has been the most fun out of the entire project. Looking at IPA strings, and figuring out why some speech has or hasn’t been classified as some word is quite fascinating. For example, I encountered a string pɾilifɛra which the model output after me saying prizim and fɛra , the two magic words that I made up for the spell system. Surprisingly, my algorithm said the string contained just prizim despite there being a literal fɛra in it. As I found out, pɾilif is closer then pɾili to prizim , and it fell under the threshold, so the algorithm took that part and left out ɛra in the buffer which was too far from fɛra . After seeing this, I made the algorithm scan the string for exact matches first, and then do fuzzy phonetic matching, also ensuring the order is preserved. I also tuned the thresholds, adding more and more tests (there is now 8 of them) to ensure the algorithm behaves well. Finally, I noticed the IPA output from the model changes based on audio’s offset in time. So I wrote a script that lets me test this, and see the effect of different offsets. What’s next There is a saying in data science: garbage in - garbage out. This is also true for my project. The detection works decently, but it also quite often outputs false positives during normal speech. I believe I did I all could to compensate for the unsatisfying quality of the speech to IPA translation model. Now I will be shifting to the Minecraft part of the project - creating a mod, integrating the detection system into it and creating spells. In paralell, I will be exploring the possibility of finetuning the model, to improve the accuracy of it and hopefully get better results. Watch the video with audio, that’s the part that matters right? :)
Integrating Rust IPA word detection with Minecraft With the IPA based magic keyword detection (which is written in Rust) working I spent all of this devlog integrating it into Minecraft. I created a new repo for the mod, using the Jared’s template. I decided to go with supporting both Fabric and Neoforge, and picked the 1.21.1 Minecraft version, though in the future I’m planning to move to 26.2 or later. I set up an additional gradle subproject and a rust package, that both together act as a Java to Rust bridge, using the j4rs crate. Using it I managed to initialize Sepple (the detection system), and get it running. The tricky part was going the other direction, to pass detected words from Rust to Java. For some reason Rust wasn’t detecting the JVM and was trying to spawn a new one! After a bit of experimentation I concluded, that I can only call Java from Rust only during the period when Java calls Rust, and only on the same thread. In the end I managed to get it working, by spawning a new thread on the Java side, and calling a blocking Rust function, that kept calling a Java callback passed to it. Originally I tried to use a static function as a callback, but for some reason (probably some kind of classloading isolation) the system was failing with some crazy null errors. I also had to adjust Sepple a bit, to not require a bunch of random json files at specific paths, and to provide an mpsc chanell directly outside its pipeline, for less boilerplate. With that I have a proof of concept working! Minecraft is receiving the detected words and printing them to chat. Note that those are all custom words, created by me specifically for this mod. This is why I had to create an entire new system for detecting them, and why they are written in the International Phonetic Language, which is also why they look a little weird in the minecraft’s font. The Java<->Rust setup is also not very good yet. For example the packaging of compiled Rust is very basic, and you can’t control the word detection from Java - no way to stop it or restart. There is also no good error handling, which means if a file is missing it will just crash everything. But it works, and I can continue working on the mod, and making it better!
Sephyr (the mod) A proper lifecycle of the detection system was added. The IPA model is loaded in background during game start, but the audio capture and processing start waits until you join a world, and stops once you leave it. The Rust code now has a logging hook, so that it can do civilized logging using the Java logger, instead of blatantly writing to stdout. Sepple (the voice casting detection system) I made the pipeline system cancellable, so that the mod can cleanly stop the detection, which was not possible before. Support for running the speech detection inference on GPU was added as a feature flag. For now it is not used by default, as it showed no measurable improvements over running on CPU on my PC, but it might be useful on other, weaker devices. Collecting data for training As I said too many times, the speech to IPA model used for magic speech detection is not quite the quality I would like it to be. I’m hoping to improve it by fine-tuning it on its weak areas, but for that I need some data. My idea is to use wiktionary as a data source. It is a free online dictionary run by the same people who run Wikipedia. Most words on it have the pronunciation attached both as an IPA and an audio file, which makes it ideal for the task. I downloaded a preprocessed jsonl dump file from kaikki and wrote a script to extract just words and ipa from it. Unfortunetely it was missing urls of the audio files. So I had to download a raw xml dump of the entire english wiktionary, and spend few hours writing a script to extract them. Then I used deepseek a generate a third script, to download all the audio files from a list. There is going to be almost 20k of them, and the script is running right now as I’m writing this, and it is going to take multiple hours as I can only download around 1 file per second to respect rate limits and not overburden wikimedia. And just now while writing I realized I’m an idiot, because kaikki provides a 20GB tar file with all the audio, downloading which would be more efficient.
Note: This one is kinda boring, mostly plumbing stuff to get things to work, feel free to skip it. Sephyr (the mod) I’m working on setting up the the logic for registering and casting spells. This is quite hard, as the design of the mod is far from complete, which means I don’t even know what exactly do I need. I created a registry of elements, which are a source of a color, and are going to have other effects in the future. The first word of a spell is its element. For example in the spell ʔalɪˈvɑn vɛɾtɛɾ ɛvi the word ʔalɪˈvɑn means wind. I also made a basic system for figuring out the spell the player is casting. For now its very simple and can’t handle more complex spells, for example, with optional words. The entirety is a big mess as I’m figuring things out, and the fact I’m making this mod for both Neo and Fabric doesn’t make it any easier. I also updated the mod to Minecraft 26.1.2, which means there is going to be no 1.21.1 release. Sepple (word detection system) I keep working on collecting data to finetine the speech to ipa ml model. After discovering a better way of getting the audio files while writing last devlog I scraped the download script, and instead replaced it with a new one to extract needed files from the 20GB Wiktinary audio dump tar. I also cleaned up the entire code, turning it back to human standards, after a bit of Opencode usage. (Since those are one-time scripts to extract data I decided using a bit of AI in this area won’t hurt. I don’t use AI for the rest of the project.). I also prepared a fourth script, that will use the existing multipa model, to generate IPA transcription of the files. Then files with generated IPA being too different from original IPA will be discarded, as a way to detect files, where the IPA doesn’t match the audio (because the audio contains more text for example).
SPELLCASTING WORKS! The main mechanic of the mod is working! YOU CAN CAST SPELLS WITH VOICE! Finally! It took soo long… After over two months of development, I finally have a proof, that my crazy idea actually works, and was worth trying out. As you can see (and hear) in the video I implemented a simple wind leap spell, as the first one in the mod. Note: There are no visuals or sounds yet, but they will be added, and this is just a proof of concept. The spell comes in three variants ʔalɪˈvɑn vɛɾtɛɾ ɛvi ˈonːdˌoː - wind launches you forward ʔalɪˈvɑn vɛɾtɛɾ ɛvi ãntikɔ - wind launches you backward ʔalɪˈvɑn vɛɾtɛɾ ɛvi fɒksɑm - wind launches you up (no matter where you are looking), not shown in the video Additionaly, it can be cast as just ʔalɪˈvɑn vɛɾtɛɾ ɛvi , which is a shortcut for the first variant. This can seen in the third attempt, where the system didin’t catch onːdˌoː , but the spell worked anyway. This flexibility is easily possible, thanks to Mojang’s brigadier library. Brigadier powers the / commands in vanilla, and now the spell syntax and execution in my mod. I settled on it after trying two alternative approaches and failing to make them work. After all, my spells are just sentences, with the only difference, is that you speak them instead of writing.
Wind spells have sound now Using LMMS and ZynAddSubFx I made two wind sound effects, which now play when casting a wind spell. You can hear them in the attached video. Cross-Compiling is fun The speech recognition system is written in Rust, which compiles down to native code. This means that it needs to be compiled separately for each operating system, and processor architecture. Rust is more friendly to it, then C or C++ for example, but as always, compilation finds it own weird ways to break. I took the setup used by Sable as a base, with two Docker containers, one for Linux and Mac, and one for Windows. BUT: The Rust version installed in the docker image was not new enough for one of my dependencies - I had to update the rust toolchain in the container image. Compiling for linux required ALSA headers for audio access - I had to figure out, how to install them in a way, that works for both x86_64 and arm64, without breaking each other. The latest version of the base image has Mac cross compilation broken - I had to use a previous one. The image ships a MacOS SDK from 5 years ago - too old, doesn’t work, so it also had to be replaced. - the container is kinda becoming a Ship of Theseus xD My code was pulling openssl as a dependency, which was failing to compile - I had to spent over an hour, figuring out how to get rid of it from the dependency tree, discovering I can do so by updating a dependency to an unreleased version. Gradle obviously required some tweaks, who doesn’t love Gradle right? In the end I managed to get it working, which brings the mod closer to being usable by the public, outside dev environment. Downloading the model The mod works by running an IPA speech recognition model locally. The model weights around 1.2G, making it too large to be embedded in the mod jar. So I added automatic download support - the model can now be downloaded automatically on the first time an user runs the mod. There is even progress tracking, though it’s not hooked up to Minecraft yet. A Toast is also shown when the loading fails, making the mod more user friendly.
The Stack Overflow For some reason my mod broke and started crashing during startup, and it did so in the worst possible way - without any error message! All I got in the Intellij Idea, was a message that the process exited with the signal code 11. WHAT? That signal corresponds to SIGSEGV signal on Linux, which generally means you did something wrong with the memory. But this is Rust! There shouldn’t be any memory issues! I started searching, and I learned there is a tool called coredumpctl, which lets me access a special dump file generated in such cases, and analyze it in a debugger like GDB. So I did that, and what I found was even more bizzare! The debugger was pointing to a start of a function, before the first line. I added some debug prints, and to my disbelief, the print before calling that function ran, and the the second one, which was the first line inside the function didn’t! Fast forward a bit I managed to figure out the problem, with some help from a friend. It turned out I encountered an stack overflow! When the function was called, Rust tried to expand the stack, and crashed because the stack ran out! Why? Because the code was loaded in Java, and Java by default allocates less memory for the stack ! The solution was manually requesting a bigger stack size during thread creation in Java. I also discovered, why the problem appeared - in the Rust file download code I had allocated an array buffer of 1MB. It landed on the stack, and that was too much! The solution was using a Vec, which lives on the heap instead. Stupid problem, but took 3 hours of my time 😩 Mod init progress reporting Sephyr requires the IPA speech recognition model to work, which needs to be downloaded on the first run, and subsequently loaded from the disk. The logic was overhauled providing more corectness, and hooked up to a series of toasts. File download progress bar is shown, and the intialization state (or failure) is coummunicated to the user! See it in the video
Visualizing the keyword detection pipeline I’m not happy with the accuracy of the magic words detection system. It work half of the time, sometimes better, sometimes worse depending on the word. This make casting longer spells quite troublesome. Debugging why a specific word wasn’t detected is hard, debug prints can only get you so far. So I decided to go on a crazy sidequest and build a gui tool, that will help me debug this. I spent over 2 hours designing the concept, and over 10 implementing it, and it’s around 40% ready. The tool will display 8 strips / plots, and each plot will display some data, all on a shared timeline on the bottom, that will update live as the audio is processed. Strips 1, 2, and 4 will display waveforms of the audio at different processing stages. Strip 3 will show Voice Activity Detection score (which is used to filter out non-speech). Strips 5, 6 and 7 will display detected IPA tokens, accumulated text buffer, and detected words respectively. Strip 8 will display latency stats, which will help in optimizing it. This way I will be able to easily see, “oh, this phoneme wasn’t detected” or “oh, VAD cut out this word” or “oh, some mess accumulated in the buffer” and tweak my algorithms. AI - it’s bad, or is it? Despite looking quite simple on the surface, the app is still quite an undertaking. And it’s just a debugging tool! A thing to help me get the main goal done! I don’t want to spend too much time on it. So I considered using AI as a tool ( NOT vibecoding ). I hate vibecod(ing, ers), and I’ve always steered off AI, because very often I wasn’t satisfied with the output quality. I take pride in writing good code. But recently I’ve been trying out OpenCode here and there (on free models), and was quite surprised with it’s capabilities. Sure, it’s still often slop, but a one you can quite quickly correct, and sometimes be more effective if used correctly. AI - is it helpful? So I decided to do an experiment - can carefully planned AI usage help me write the debugging tool quickly? I started off by designing the functionality of the tool - I described the idea in detail to MiMo-V2.6-Flash, and asked it to interrogate me about as many details as possible and needed. I had to answer 35 questions, some of which were quite observant, picking me on nooks and crannies. After that a markdown file describing the design was written, and then went through few corrections. Next up, in a new session I asked MiMo to create a bullet point outline of steps needed to be done to implement the tool specified in the design file. Then one by one I went over each bullet point, asking the just released LongCat 2.5 (which has a bigger ctx window) to ask me questions, and formulate an implementation plan of it, present it to me, I gave corrections, and the point in file was replaced. This way I ended up with a second markdown file - an implementation plan. This took way too long, and was really boring. So, finally coding time. I do it like this: Ask opencode to implement a step of the plan Review the diff in vscode Remove fluff and slop manually, make sure all new code is good quality. Give feedback if I something doesn’t work like expected, or also fix it manually. After it’s good - commit Repeat for the next step It’s still not super fun, but at least I’m touching the code now, so much better already. And it is efficient! I believe it definitely would take me longer, if I wasn’t using AI. So the morale is: If you want fun, or doing something important - do it manually. If you want to do something quickly and get it out of the way less important. Here is a screen of how it is looking now: the general shape is in place, but there is no plots yet.
Keyword detection UI is done Look as this! It’s so pretty! Seeing all the data visualised is quite mesmerizing. I know the image will probably attract more views, so lemme explain what you are looking at. I’m making a minecraft mod where you cast spells by saying custom made up words, and this is a tool for debugging the keyword detection system. It’s meant for me, to help me make the detection more accurate, and reliable. I’ve been building it for the last ~20 hours, and in the last two I built the rendering of all the 8 strips. Check previous devlog if you want to know what exactly they display. It was pretty smooth sailing. As said before, since this is just a debugging tool for me, that I want to have finished quickly for it I’m using AI assistence, by letting it do stuff in small, pre-planned steps, and unsloppifying all the code between them. It’s working well. What helps, is that entire app is built on the egui / eframe crates, which I used before, and are somewhat familiar with. I gotta give credit, where is due, their api is really intuitive for the most part, and simple to use. The entire chart is also zoomable, and scrollable, and making this work was a bit of pain. I wanted to limit the accesible range to only around the data (so you can’t scroll away to far from it), and egui_plot doesn’t really provide a way to do it, so some manual calculations had to be done. I wanted to have some breathing space by allowing 0.25x of the zoom after last audio sample to be visible. This was the worst part, as it’s kind of a recursive problem - the zoom value affects how much zoom is allowed. But after around two hours I manged to figure it out, but keeping the original zoom value independantly of the limit calculated from it. Worth noting - what you see is just a mockup data, and the gui isn’t actually hooked up to the detection system, as that’s what I’m going to do next. After that I can hopefully improve the detection, and the finally go back to minecraft coding.
Debugging gui is (almost) done What is going on, why already 10 hours passed? This was menat to be a quick sidequest, where I built this gui tool for debugging how well detecting magic keywords is working BUUT given it is the third devlog about it That means it took over 30 hours! Well, happens. So, all of this devlog was spent building all the plumbing needed to capture all the data from the detection pipeline and sending it in realtime to the app. All of it is using done using channels. They are injected into the detection pipeline either as Tap<T> ‘s - a new pipeline element that clones data it sees, sends to channel, and passes it on, or using wrapper types around existing processors. The app then receives the data and displays in on the plot. I also encountered and fixed few bugs, that I didn’t expect. For example originally the zoom went to crazy values, because a timestamp used to determine where the data ends wasn’t converted to be relative to the timeline start. I also found an quite an issue introduced 3 months ago, where the timestamps attached to audio weren’t calculated properly, making entire audio chunks have identical stamps. It was minor enough that I never noticed till now, when the data landed on the plot and resulted in this funny thing you can see on the attached screenshot. Finally I added a simple scrolling animation, to amortize the jitter caused by bigger chunks of audio coming, so the plot doesn’t jump that much when going live, but it’s still not as smooth as I would like it to be. As mentioned before I was using AI assistance in strictly controlled manner (I carefully planned, revieved, cleaned up and adjusted to my liking all the code generated by it) to build this debugging tool faster, and by now I’m very tired of it. At the end of the work it started doing stupid things (possibly because some context was lost?), which made me question if it was making me faster or slower. Luckily the work is mostly over, and I can finally get to other, more interesting stuff, that will be fun to do, with no AI. Next I want to use the tool to improve the detection a bit, and spend more time working on the actual mod.
Multiloader modding is pain Can’t NeoForge and Fabric just have one common api, for all the boring stuff like registration, networking and events? I’ve had enough, so I spent 2 hours porting my mod to Balm , which is a library providing exactly that. The result is beautiful - ~150 lines are gone, and the loader specific subprojects fit under 10 lines of code, as I was able to move everything to common. Now for the worst irony: I spent my time doing all of that and the next thing I’m gonna do is… disable fabric support 😭 Why, you may ask? I was forced into it by reality read on First VFX I made my first VFX, for the leap spell. You can see it in the attached video. It’s going to spawn under player’s legs. For now it’s a really simple one, as I’m rushing to add a bit more of content, than just a single spell, before Stardance ends. It is made using Photon Editor , which is a VFX editor for Minecraft. It has a lot of good stuff, like emitters, timeline and animation curves, which will be crucial in making more advanced effects. Only downside: It doesn’t support Fabric, which is why support for it is going to be disabled for now, until I manage to hopefully make a port it in the future. Reworking spell internals (again) After making the effect, I realized I have no way to run it from the spell code, because spell logic runs server side only, and VFX needs to be played on the client. So I wen’t back to the drawing board, and I’m going to change how spells run. They are going be entities now, but more on that next devlog, because it’s not done yet, as I ran into friction in the code, which triggered me to do the Balm refactor, and in the meantime the time counter hit 9,5h. Do NOT hardcode stuff please I had to do some work on my laptop today, which has NixOS. So I update my Sephyr clone, try to run it, and bada bing bada bum, it doesn’t work! What’s going on? After a hour of debugging, it turned out that NeoForge devs hardcoded their Gradle plugin to run one specific task using Java 21. The rest runs on Java 25 (the one used by Minecraft 26.1). So, be default, a NeoForge setup for Mc 26.1+ requires two java version. Normally the second one is autodownloaded, but that doesn’t work on NixOS, and that’s also bloat. I don’t want that! So I made a bug report . Fortunetely it isn’t really hardcoded, just dumb, and I was able to fix it with this line in build.gradle Update to Burn 0.22 Burn 0.22 was recently released, removing the Backend generic from most of it’s traits. I updated Sepple to work with that. I also had to regenerate the model code for the multipa model. I was hoping the update would provide some speedup to speech processing, but I wasn’t able to measure any difference. Replacing Multipa with Zipa I found a new speech-to-IPA model. It’s called Zipa , and it looks very promising. It’s around 4x smaller then multipa, which means faster processing and less to download for end users. Furthermore, according to it’s paper, and my few tests it has noticeable better accuracy! So I implemnted it. It was a little more trouble then multipa, because the model has a much richer pre-processing. The audio waveform is turned into a mel spectogram, Fourier Transform is done, and other stuff happens. Buuut there is a problem. It turns out the model is great at doing recognition on longer audio sequences, and doesn’t really work that well on short ones. For them it performed worse then multipa! That means it’s not suitable for doing recognition in realtime, at least when plugged in the same way multipa was. However I have a couple ideas to try, which might, hopefully, solve the issue. Until I do that, it is set aside on a separate branch....
|
1792641292.6978648
|
1793246092.6978648
|
|
21103
|
Ritualia by @gabriel_ferreira | Stardance
|
gabriel_ferreira
|
1
|
148.7
|
2310
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-23T23:45:29Z", "hours": 148.0, "multiplier": 18.45, "payout": 2310, "blessing": "neutral", "text": "What did i made I made an complete music play system that will help users to learn while listening to musics! I used youtube, flask, python, mysql, js and hope to make this! What else Solved a lot of painful bugs that were stopping the project from working Finished and made the music queue work Added music description labels Improved a lot the performance Made the website responsive Added a keyboard based navigation Fixed so much errors at the cover-flow that now i forgot how many Added bug checks before the play of each video on the player Created an toast component to show errors or infos Added 54 musics to choose and listen Challenges The most painful code was the player and the cover-flow, they were with a lot of hidden bugs that i had to figure out, feats that i had to make and pain to suffer! Now i figured out that i love and HATE making a website responsive! What im proud of MutationObserver: An observer that loads / hides an image based on its position at the cover-flow, this system took a while and worked very well. Error checks at player: The system can handle the error that happen and dont crash! Keyboard navigation: Everyone can use the keyboard to navigate now! Oracle VM: For the first time i used an ssh and hosted an website in my own machine (oracle, not mine)! How to test Access http://ritualia.duckdns.org/ , when you join and the page loads you will find a screen to choose an genre, select which one you want, after you will see an emotion selector, select the emotion that you wanna feel! You will see the player screen and an audio explaining the music (The audio is in portuguese), after the audio the music will play. Notice: The github and the website are in portuguese, please use your browser built-in translator or any that you want to use the software properly! Follow the installation guides at github if you want to install", "repo": "https://github.com/GF-Silva/Ritualia", "demo": "https://ritualia.hackclub.app"}]...
|
[{"post_id": 73067, "ts": "202 [{"post_id": 73067, "ts": "2026-09-24T09:47:16Z", "hours": 0.6983, "text": "Added english readme I created an english readme, api and install Guide, so it will be easier to read and more accessible for worl-wide users"}, {"post_id": 72881, "ts": "2026-09-23T22:36:56Z", "hours": 0.3906, "text": "It is time to ship! Now the system is working perfectly fine running at a vm in oracle cloud with a public domain, and a dns with duckdns at http://ritualia.duckdns.org/ Warning: It is just HTTP!"}, {"post_id": 72810, "ts": "2026-09-23T19:36:13Z", "hours": 1.3461, "text": "Configuring an ssh server! I configured an oracle cloud vm with ubuntu to host my website 24/7, that was because nest wasnt working in a while. Im running it at a local IP with http only, i will configure https today and maybe an domain, the IP is http://164.152.55.252 , notice that some browsers may block you from accessing and http website. UPDATE AFTER THE DEVLOG: I put an domain using duck dns: http://ritualia.duckdns.org Hardwork I had absolutely not a single knowledge about oracle cloud vm, so configuring it was an hell. Sometime my internet stopped working so i had to use my smartphone, this lead me to configuring an invalid ssh key and i had do configure everything from scratch, i spent 5 hours configuring everything, but that was ssh and wasnt tracked. But now everything is working fine. Thx oracle This vm is a 24/7 entire OS (Virtual Machine) that has some good free plans, without any cold start. Thx stardance Stardance now will have one more mount!!!!!! I still has roughly 40 hours of deep development to do after this ship, i will love to finish that in stardance!"}, {"post_id": 70936, "ts": "2026-09-20T02:22:48Z", "hours": 2.9706, "text": "Added install guides Now there is guides to help the user install for ubuntu, arch and windows. Notice that im a linux user and my pc is weak, so i could not test it on another operational system. Also the guide is in portuguese (all of them), please use your browser built-in translator or any of them to read if u dont understands portuguese I could test it on another linux distro, but i would have to go through all the install steps. Also i cannot try installing windows because it normally wipe out another OS installed on the same disc (i know that it just replaces the bootloader) and using a VM was not possible due to the amount of RAM and the processor that i have."}, {"post_id": 69913, "ts": "2026-09-18T01:53:38Z", "hours": 0.3256, "text": "All done, it is time to ship Every metric at lighthouse it nearly perfect, im so exited to ship it!"}, {"post_id": 69904, "ts": "2026-09-18T01:19:42Z", "hours": 4.9561, "text": "The accessibility workings is done Features Now the system has a new navigation system that allow users to navigate with just the keyboard! (The video was with just the keyboard) Why Why not? It was meant to improve the accessibility for users without mouse, or any another problem and because i was tired of having to use the mouse to debug something. How it was made Again with a lot of pain, the feature itself was easy but i had to fix some errors that went unnoticed, so it took more time. The feature detects when the user press a key and do an action based on wich key was. The accessibility was easy to get 100, but the metrics at lighthouse dont give all what the users will need, i had to think about the website usage and how would be the easiest way for the user. Results The accessibility went from nearly 69 to 100! What next Now i will work on the best practices! Update after the devlog: Nearly every metric is already maxed out lol, i will ship it very soon!"}, {"post_id": 69444, "ts": "2026-09-17T02:13:13Z", "hours": 1.6906, "text": "The performance workings is finished and bug fix Fixes Now the logo z-index is lower, that is because the logo was ‘eating’ the click in more important elements. Also i did a small change at the cover-flow, that change fix the bug of the scroll at the element sudently stoping when at a certain position. The copa script had some old references to images that were leading to bugs, i changed it. Performance I already finished it in the past devlog, i was just checking if there was anything worth improving, but everything is working fine and now is time to improve acessibility. Most of the logged time was me trying to improve more some metrics at the lighthouse, but the work was too much for anything that is already good, so i decided to stop before i started focusing too much in it."}, {"post_id": 69395, "ts": "2026-09-16T23:43:00Z", "hours": 1.505, "text": "Fixing an error at the youtubeHelper Now the youtubeHelpers checks if the youtube API is ready before sending commands. Why Sometimes the API maybe is not finished yet, that was leaving an untreated error that was braking the entire website. How was made With a lot of pain and suffer. I thought “Oh hey, that will be a 15 minutes fix” - i spent 1 HOUR FIGHTING with the iframe itself, nothing was working and at some point i started thinking that even the API of YOUTUBE wasnt working, even the most basic function that should be triggered when the youtube finished the loading wasnt beeing activated, even the console.log(“Api READY!”) wasnt working, even when it worked it wasnt working well, the variable of the player was a promise and not the result. At the end i shut down my PC, defeated, without any energy left, with just hope that in the next day (this) that would be fixed (if not i was planning to ignore and make it a feature “Easter egg, if u dont join the website for a while it will not load!”. The problem here was just the position were the iframe api was in the HTML, it was being loaded before the helper script, so the helper was not there to check if it the iframe was ready or not, the fix was just move it a little down. Architecture It was just an promise, if the function checks that the iframe isnt available it await for it to be available. The check if it was available use the onYoutubeIframeAPIReady, easy easy (except for all the suffer that i went into)."}, {"post_id": 68896, "ts": "2026-09-15T23:59:43Z", "hours": 1.1389, "text": "The loading is done and working! Fix I fixed the error that were happening when the cover had a certain amount of elements. Problems The fix itself was very easy to do, but doing it in a readable or without using 10 lines of code (i want things concise) was way harder than i expected. So i spent this entire time trying different ways to do it, each of them making everything worse. Reasoning The bug were with less than 14 elements, the script were calculating the first and last element index, it happened that these elements were behind the screen focus. Why? Because the values that the system was using were leaving these values, so i had to fix the way it was calculating things."}, {"post_id": 68865, "ts": "2026-09-15T22:15:27Z", "hours": 1.3303, "text": "The loading is done Features Now the loading at the mutationObserver is finished, it finally loads or unloads the images that should be (or not) loaded! The new observer is way faster, because it can detect the image way before it enters the screen, loading it before and having more time to do it. Also the system loads just a maximum of images (with the configs that i put it is 13), so it will not fry an cellphone. Problems The loading part was easy to do, i already done it before in the past observer (intersectionObserver), so i just had to pass the old code to the new part. But when the cover-flow element dont got atleast 13 elements the mutationObserver code breaks completely! Im still pondering how to solve it, when i do i will post about! Results The performance went from 78 to 100! The system is way faster! The user does not see a blank image anymore! Why The old method was slow even with a good internet connection, because it was loading the images just when the element was already in the screen, leaving to a perceptive delay for the user and a blank image to the user. Also i thought about creating an observer from scratch (maybe i was a little off my ideas, because that was insane), but the browser already had exactly what i wanted (the mutationObserver) Videos The new features The performance of the new feats The old features Reasoning While i was trying to sleep i thought about a system that fill the cover-flow with a maximum of images (like 10, or 13, or the code decides) at the DOM, using less memory to run. That system will free a lot of memory based on how many cards the cover-flow have. But that will have noticeable effects just with a lot of elements and probably would be hard to develop, maybe i will do in next updates. What next Im leading to the shipping, so i will improve metrics about the lighthouse, improve the readme and post it."}, {"post_id": 68449, "ts": "2026-09-15T04:25:35Z", "hours": 7.2278, "text": "Adding a mutationObserver Now, instead of an intersectionObserver, the system use a mutationObserver that monitors the angle of the cylinder itself. With that the loading of the images will be insanely better, faster and user-friendly. Now the changes in the cover-flow is observed at the cylinder angle, not at each card. There was some problems in the journey like, for some reason, unexpected cards being loaded or not, the loading script at the observer dont triggering at all (that was caused by the speed of the observer) and an insane amount of cards being triggered at the same time (i still did not figured out why, but that isnt ‘harmful’). But man, i had to be creative here and remember about some concepts like concurrent programming, buffer / queue and apply some delays (because the mutationObserver is insanely fast and can trigger every millisecond). That all was meant to decrease the speed of the mutationObserver, because it was triggering with the most small change in the cylinder, so i made it trigger just when an expected amount of movement was made. The red/yellow collor is for me to visualize the loading state, it is just for debug The code isnt finished, i still need to implement the loading part, for now i just implemented the observer and basics of the loading part"}, {"post_id": 67841, "ts": "2026-09-14T03:19:48Z", "hours": 4.2825, "text": "The observer is finished (nearly) Now the cover-flow has an observer that checks the position of the images and changes it styles based on that, so the images that the user is seeing is loaded while the others is hide. It still has some bugs and space for improvements that im still figuring out how to solve, like the image loading too late on the screen or, in slow internet connections, the image takes too long to load. Most of these bugs are because the observer (an intersectionObserver ) was designed for 2d contexts, not 3d (that were and still is an giant problem to solve). For this reason i was thinking about creating an observer from scratch in C++ that can work better in my 3d element, but the overhead would be high so im trying to solve in other ways before that. The feature was made using fetchPriority, contentVisibility and src (the system add the source just when the image is near the screen). OBS: I found about MutationObserver after this post, maybe i will use that instead of insersectionObserver. Oh man, at least if had found about this before… Aaaaaahhhhh i spent 9 hours in that, but at least a Lot of the new logic will be used. Anyway, lets cook"}, {"post_id": 67151, "ts": "2026-09-13T06:53:12Z", "hours": 5.4219, "text": "Adding an observer Now the script observe the card elements at the cover-flow component, classify their position and changes it visibility based on that. Im using an IntersectionObserver to observe every card, but it was designed to observe the position of 2d elements, so i had to figure out how to make it work well in an 3d element. The way it works is when the observe activates (it basically means that some card is in the range to load) it get the card info (like position and angle) and changes it visibility state based on that. Simple, right? No, it was a headache for the start to the top, nearly 4 hours of what i did was to fix the fact that it was meant for 2d. The observer were activating for cards that were in the back of the element, so i had to classify the card position into back or front by using its angle. But if i tried to use the card angle without anything else the script would broke (and did for 3 hours until i realized how to fix), so i had to use an second angle which is 360 - cardAngle (the 360 is the angle of an circle), so this new angle is based on the circle end while the other is based on the circle start. But the system still broke, because at some point one of these values would become negative and makes the if that classify the elements fail. The fix was using the absolute value in the if (it took 1 hour until i figured that; oh i were so happy when it worked). Why To improve the performance, so the browser will just load what is really important and not 50 images that the user cant even see. What else All that work definitely improved a lot my logic thinking and for the first time i used the truth table well. Now i just need to use the new code to change more the style of the images that are not in the screen. I did not deleted the code that i wrote in the past devlog, it was used in the new code to select how many images should be loaded in the same time. ** The first video is the result, the others is some problems that i had **"}, {"post_id": 66316, "ts": "2026-09-12T02:31:12Z", "hours": 4.5481, "text": "Improving the loading performance Now the system selects some images that needs to be shown faster than the others and making them load with priority. It was made by selecting the images that the user is seeing and defining some params like fetchPriority = \"high\" and decoding=\"sync\" , the other images have the params: decoding=\"async\"; loading=\"lazy\"; contentVisibility=\"auto\"; . With that config the LCP went from 2.4s to 0.5s . Also it loads the image in the start and dont change that config again, so the browser will be lazy while downloading these images. The problem is: only the first images are chosen as high priority, but their positions will be changed while the configs stays the same, that makes the browser lazy while loading them and makes the runtime performance became worse. To fix that i found about IntersectionObserver, that are meant to solve the problem of updating the images position fast. It was not applied yet, i found about it like 1 hour ago. What else Now the perspective of the emotionDrum is based on the elements that it have, before it was based just in the screen and would broke if something changes. I did a little change in the way that the script checks for clicks in the screen, now it has more accuracy (I changed the round system)."}, {"post_id": 64533, "ts": "2026-09-09T00:41:52Z", "hours": 7.6869, "text": "Improving the LCP (Largest content paint) performance I am improving the way that the website create and downloads the images, the main reason is to make the user see something faster. Before the system were doing 2 main things: Go trough the entire list of images, creating the elements and placing them into the page. Position them. But the position of the items were being done after the creation of the images, in another function. Now they are in the same function: it create the element, position it and add it to the page. That does not seem like much, but the code got way faster with that, probably because it dont need to go item by item in an list twice just to create an element. I tested the time to load the first element and it went from 8.63s to 6.43s , the test was made with 2200 elements (Yeah ik, it is a lot and normally it isnt that much, but i had to see how they were holding with more than 22 items). Also i fixed a bug that passed unnoticed while improving the responsiveness: At some point of images the visibility of the elements broke, that was because the perspective (what makes the 3D perspective of the component, and makes it visible with 3D effects) was based on the screen size, not the elements itself. What was the problem in that: Each element takes a size in the screen ‘makes the circle that shows the element be more and more near of our point of view’ (i cant explain it any better, search what is ‘perspective in an 3d element at HTML’ ). While it was approaching our point of view (POV), our POV were exactly at the same position (based on the screen size), so at some point the circle perspective would pass the POV and we would be unable of seeing it well. Now it is based on the elements: The 3d elements were basically a circle, i just used the diameter to change the perspective based on it. TODO: Maybe i lowered the image quality too much… I will fix it in the next updates. Also i were trying to improve the font settings to improve the performance, but that wast working so i decided to move on to this because it was more critical (took a while for me to jump to this, maybe like 2 hours trying and trying values in the font settings). i am also working in a way to load the images in parallel, with that it will take way less time (I didnt known about LCP before so i spent a while figuring out the way it works)."}, {"post_id": 63656, "ts": "2026-09-07T16:11:33Z", "hours": 2.6633, "text": "Improving the performance After finishing the improvements in the responsiveness, now i decided to improve the performance. I used lighthouse to get insights (i learned about it in an tiktok video) and figured out that the images size were too high (were an png of 1280x1280px), so i saw more about that in the page: https://developer.chrome.com/docs/performance/insights/image-delivery (an link that google lighthouse gave). With that i saw how to decrease the size while staying with quality, i reached up to the sharp-cli tool, that makes the work of resize, convert and compress the image for me. The command used was that: npx sharp-cli -i * .png -o ./output resize 320 -f avif -q 50 --effort 6 --smartDeblock --preset photo The 320 is the size that i want it to be at the end, i got that value by seeing what was the computed value (the value that is being used in the website) in an high resolution pc (not the most higher one, the high but normal pc) with the resolution of 2500x1714px . With this simple changing the image went from 2mb to 40Kb! Nearly 100% decrease, and take in mind that there is more than one img, so the change were even higher. Also i created 5 different images: 320px, 480px, 640px, 960px e 1280px. With them the browser can choose which one is better for each device. With these changes i went from 35 points of performance to 59."}, {"post_id": 63459, "ts": "2026-09-07T10:10:31Z", "hours": 0.8717, "text": "The home is now fully responsive Ah that took a while, but finally it is fully responsive. Now the description at the player UI resizes based on the screen size and active a scroll behaviour when need, also i did some minor change at the emotionDrum perspective to make it show more emotions at the same time, removed the padding in the arrows at the emotionDrum so they will have more space and fixed an error in the genre drum that was making it perspective broke. After all these devlogs i improved how i make a code responsive."}, {"post_id": 63436, "ts": "2026-09-07T08:55:04Z", "hours": 2.5378, "text": "The emotionDrum is now responsive! Now the buttons that you use to select the emotion are in the same side as the div that displays the emotion, so they dont fight for space anymore. This change is meant to fix the low size of the items in screens that are too small, in the new way we have more free space to display the emotions, there is not much to say. What else I did some minor changes in the size of some items at the player area, now they are easier to read! (I increased the size of some items)"}, {"post_id": 63379, "ts": "2026-09-07T05:17:01Z", "hours": 6.6125, "text": "Im nearly finishing the workings on the responsiveness. Now the entire player screen is responsive and do not breaks the texts anymore (except for the description area), also the buttons that show you the next screen (the ones that displays ‘player’ and ‘generos’) are responsive too, the music queue is working perfectly fine, and the area that is used to select an emotion is nearly responsive too, it just needs some adjusts in the two buttons that change the current emotion because they become too small and uses a lot of space when the screen is too small, after that this screen will be finished. It is taking a really long time, the fact of how many different screen sizes exists makes it harder to finish, but now there is times that i even cannot see if i changed the resolution in some areas, because the system looks the same. I spent quite a while testing different resolutions and combinations of size, i didnt even noticed that i where 6 hours here, but now ik more about css (things like container-type + container based sizes and more ways to set the size of an text) The video shows how it is now, if u want to see the before just checks the past devlog."}, {"post_id": 62723, "ts": "2026-09-06T06:59:19Z", "hours": 0.7094, "text": "Finished the correction of the errors at the perspective and click in the cover-flow component. Now the perspective stays the same 150vw value (that is based on the screen width) and stays normal, with everything working. Oh man, that needed a lot of logic and out of box thinking finding another ways to check where the user clicked without using an normal click event listener (for those who lost the past devlog or didnt understood, i cannot use a normal eventListener in that because the clicks is detected in another element or in nothing, even with z-index or more in css, so the solution should be done entirely on javascript logic). The first video is the new way, the second is how it was before all changes in it"}, {"post_id": 62679, "ts": "2026-09-06T05:10:34Z", "hours": 5.5194, "text": "i finished the workings on the section of the home page that i where doing before, now has a good responsiveness for pc and a decent one for mobile. Now im working on the main area of the home page, the one where the user selects the desired genre for the music (second img). It may looks completely fine, but that same element already was a headache before and is here again. The given area is the cover-flow component, before i already fixed it (or so i thought), but it was looking bad (the 3d perspective where a bit strange) and would probably broken if i added new genres. I were trying to figure out a way to listen to the clicks in the card without any problems, that seems easy at first glance, but the 3d perspective in the element make other elements eat the click from one another, make the clicks dont be listened and so many problems that i nearly decided to erase it and write from scratch. I already found a way to do it by using the mouse position, the center of the screen and the card width (the genre img) to transform it in an value that can be used to select the exact card without any dependencies on the doom selector. It basically uses just some values, put in some calcs and get the difference between the current card (genre) index and the clicked. Im nearly finishing it, but i still need to figure out some values and more."}, {"post_id": 61879, "ts": "2026-09-05T01:17:53Z", "hours": 1.1772, "text": "Im improving the responsiveness at the home page (before was the player page), but for some reason that idk the emotion selection div is having a strange behavior, i will update what it is after i fix it. Take a look: (the item in the center should be centered)"}, {"post_id": 61218, "ts": "2026-09-04T01:30:11Z", "hours": 0.725, "text": "Hi again! Like said in the past devlog, im improving the responsiveness of the website, specifically in the player part. But it had some problems and where distorting when playing an video, now it resizes based on the screen size in a way that it stays ¨normal¨. The first video is the new way, the second is the old way. It still needs a lot of improvements, but it is going!"}, {"post_id": 60619, "ts": "2026-09-03T02:22:12Z", "hours": 1.4631, "text": "Making the website responsive Hi again! Like i said in some devlog, the website dont have support for mobile, that was because it was designed to be at kiosk mode and work just in desktops. but i decided to change that. First of all: the site where using a lot of absolute values of dimensions, so it where braking even in the desktops (it wasnt responsive at all). But i spent a while changing it, it is isnt ready and has some problems, but is better than before. (The mobile is still strange, i did not started fixing it yet). Credits: https://css-tricks.com/snippets/css/fluid-typography/ - This website shows about a way to make the texts become responsive for the screen using some calcs, it is worth seeing. The first video is how it was before, the second is how it is now"}, {"post_id": 60559, "ts": "2026-09-02T23:48:07Z", "hours": 0.7944, "text": "Creating a new helper Context: There was an class called youtubeFrameControls , this class basically where designed as an structure to help using the youtube iframe API, but with the time this class where becoming basically to hold just an function (and in a very inefficient way), because the methods that it had before (like the one that runs when the iframe is loaded) had to have more and more specific logic in the codes that used it, so they where useless (because the codes that where using it where rewriting it). What i did: I picked the only function that where being used and transformed it in an helpers (that can easily be customized). This function was the one that creates the player of the musics, and the function where called createPlayer . Also i removed the old class (youtubeFrameControls) and replaced it for this helper in every place that used it. It now is way more simple than before and can me more customized."}, {"post_id": 60001, "ts": "2026-09-02T02:58:53Z", "hours": 2.1603, "text": "Refactoring the copa script I where placing some scripts in a class. These scripts are related with the player functions of the copa script, so i pick all of them and placed in the same class. I did it to follow best practices, but i confess that i still got some doubts about when to create an class and when the variable or method should be static or private (#property), i will search and learn more about because maybe i will have to refactor some parts of the code following these rules of best practices. That was all for now, see ya!"}, {"post_id": 59339, "ts": "2026-09-01T02:28:57Z", "hours": 2.4744, "text": "Adding more bug checks I added some checks in the player class at the startExplication function that checks if the src (the url for the audio) is valid or not by doing an fetch with the HEAD method and checking if the return is an valid audio before trying anything in this function. Also i solved this error in the same function: Uncaught (in promise) AbortError: The play() request was interrupted because the media was removed from the document. https://goo.gl/LdLk22 This error was because the startExplication function where trying to play the audio before it was loaded, i solved it by making it play the audio just after it was load with the event listener canplaythrough . There was another error at the playMusic function where the code where trying to play the youtube video before it was loaded, i solved it by making it wait until it was CUED (Where the video isnt playing, but is loaded and showed). (The website was not created to be in mobile and dont has a lot of support, the video where recorded in my smartphone because i had to record the audio and the audio on my pc dont work. In the Future i Will add mobile suport, but right now im finishing the bugs and etc)"}, {"post_id": 58653, "ts": "2026-08-31T04:27:35Z", "hours": 2.7947, "text": "Adding a new description feature So basically there was an place where was designed to have the description about an music, but that was empty and where only finished today What it do It is petty simple, it just show an text about the music that is currently playing, also there is an audio that plays before the music starts playing, but this audios is played just one time before the music so if someone loses it or didnt listen well there was not a second chance to listen for that audio. Now there is this place that holds the same content, but in the format of an text that can be read whenever they want while the music is being played, that was what i added. Also that text can be hidden, so it will not stay using half of the screen every time (in the screenshot it is active) What this audios do This audio / text is part of the main purpose of the website, they give some fun facts about the music that may be useful someday in some kind of quiz or essay. Also the audio is played before the music and the music plays just after the audio finishes. That is to make whoever is listening listen to the entire audio before having what he (or she) wants, so they will not listen to the music and skip the audio."}, {"post_id": 58526, "ts": "2026-08-30T23:20:27Z", "hours": 1.3911, "text": "Studing about cache So basically i where studing about cache, the main ideia was to implement it in the sistem, but then i realized something: Theres is nearly no difference between cache and a normal request in an localhost project, adding the cache would not improve anything, sometimes it maybe even slow the website, because the page would need load more code. I realized it afther a while studing about it, but anyway lets focus on another thing. Also i where studing with google for devs: https://developer.chrome.com/docs/workbox/caching-strategies-overview"}, {"post_id": 56374, "ts": "2026-08-28T03:45:15Z", "hours": 1.0169, "text": "Toast applied and tested The toast notification component is now being used at the player class, i did some tests and it is working very nice, hopefully the user will not see it very often (because that would be a problem, the user must not see it unless there is an error playing the music). But why did i make it? It is used to show more about what is going on for the user, so they will not become anxious if it something is working or not. Why not use another notification system? Each notification system has its own use cases, the others would be very painful for the user. Imagine every time a simple error (that is simple for me) like the music fail to play there was an error that interrupt the use of the app with an popup or alert, that would be painful, dont it? That is why i used an toast notification, because it is temporary (disappears after some seconds) and dont stop the use of the website. Also there is a feature that i implemented for the same reason a few devlogs ago that add a fallback in the music search, so it wont show a message at every single combination. Lets calculate how many combinations we have: there is around 50 musics in the database, 22 genres and around 40 emotions, multiplying the genres and the music 880 combinations that the user can make. Now lets see how many musics we can get: each music have around 1.2 emotions and 1.2 genres, so we get 72 combinations that lead to an music . With that in mind, we can see that from 880 combinations, just 72 of them are useful combinations, so we get a 8.18% for getting a music, and 91,12% of not getting one. Consider that each failed attempt shows an error that stop the use flow of the website, but that was before the past devlog, now it tries again and again and only afther failing everything it shows an error, at that point it must really show an error. Thats all, sorry for the long boring text and thanks everyone that read, see yall later"}, {"post_id": 56331, "ts": "2026-08-28T01:58:42Z", "hours": 3.1336, "text": "The toast notification is finished So i finished it, it is a component that u adds in the code that u wants to use. I added 2 icons, 1 for infos and another for warnings. What i used and did The element that holds it all is an dialog , the icons are svg , the text is a span and the element that shows the time left is an progress . To update the time left i used an setInterval that runs at a 10ms delay and deduce 0.01 of the dialog value, so it will be smooth to see. Also there is an if inside that destroy the element and clear the interval when the value of the dialog reaches zero or less (In the past devlog i said about not using an if, but considering how low the cost for an if in that time is i decided to use it anyway). The function that creates the toast receives some params that will let it be customized by whoever is calling it: const icons = Object . freeze ({ INFO : infoIcon (), WARNING : warningIcon () }); function createToast ({ message , time = 5 , styles , icon = icons . INFO }) // the `message` is the displayed message // the `time` is the time until close // the `styles` is custom css styles for the element, it uses the dictionary format {} // the `icon` is the svg icon What else? I will apply it in some elements and post about. See you later!"}, {"post_id": 56188, "ts": "2026-08-27T20:27:52Z", "hours": 1.5956, "text": "Creating a toast notification system So rn im creating what i said in the past devlog about a toast navigation. This isnt finished yet, im still figuring out an way to check the end of the timer, i found some ways to do it, like using an simple if inside the countdown. I didnt comment but i am using an setInterval to decrease the time and an <progress> tag to show the value but that is a bad ideia, imagine doing 1 if every 50 ms? that dont cost a lot of processing power, but i still will search for other ways to do it just to check, if yall got any hints or ideas leave a comment. Also i will improve the design, the design is petty poor."}, {"post_id": 55334, "ts": "2026-08-27T02:23:29Z", "hours": 4.2383, "text": "Fixing errors and adding bug checks Hi everyone, so i were fixing some bugs that i saw that may broke the app, i fixed all of them (for now), here they are: There was a logic bug that a class (the one that controls the youtube iframe) where doing stuff that it wasnt supposed to do (not an unexpected behavior, but a logic that should be outside of it), it was an easy fix. There was a bug at the player class that when the provided sourceId (for the youtube iframe) where wrong or unable to use it crash and stopped doing anything else (like playing another musics). I tryied solving it in various ways, one was beffore the changes that i did in my last devlog, but i failed because the class that holds the player where hard to fix, this devlog fix it. I fixed it by adding some bug checks and, when some error ocurred, it play the next music. There was also a bug at the player class and youtube-iframe-controls class, that for a couple seconds in the start of the website it would broke if u tried to play a music. It was because in the start of the program the youtube API where unable to be used. To fix it i just made it wait until the API where ready using an ASYNC function. There was also a bug that, afther the past devlog, it was doing and request at the /api/songs with params at the wrong format. Afther the past devlog the API started using the params genre_id and emotion_id , but the frontend where passing with the incorrect name (genre, emotion). Something that i implemented I upgraded the mysql query that where being used to get more infos about the music, before it where just returning infos like name, author, sourceId, etc, but now it also returns the genre and emotion from that given music. I added a bug check at the player class, now if it fails it pass to the next music. Something that i want implement I want to implement a simple toast notification system that will apear when something fails, like the playing of some music. Others The youtube docs for the iframe where very helpful for this changes, check it out: https://developers.google.com/youtube/iframe_api_reference?hl=pt-br"}, {"post_id": 54690, "ts": "2026-08-25T19:06:05Z", "hours": 3.2633, "text": "Optimizating some piece of code So there was a certain part of the code responsible for the songs queue in the main route, that piece is a core function but where running a while true loop for no reason , here is the part: async start () { while ( true ) { // { emotion, genre, author, name, sourceId } const musicData = await this . #musicQueue . get (); this . createPlayer ( musicData [ ' sourceId ' ], null ); this . #showMusicInfos ( musicData [ ' name ' ], musicData [ ' author ' ]); this . #queueList . removeChild ( this . #queueList . children [ 0 ]); await this . #startExplication ( `/storage/ ${ musicData [ ' explicationSource ' ]} ` ); this . player . playVideo (); await this . #musicFinished . when ( true ); this . #musicFinished . set ( false ); } } Basically the code were calling it at the start and running it forever, but that code where a nightmare to maintain / implement, i tried to implement a safe check here for over 2 hours, but wasnt possible. I replaced it for a more efficient approach that do a loop in a different way, here it is: when adding a music, it checks if it is already playing, if no starts to play. when start to play it checks if there is a music in the queue for it to play, if no it stops playing, if yes it get it and play. When the music that is playing ends, it call the play function (from the above topic). That is just it, no while or constant loops, no overhead, it works by basically connecting all the codes in a way that they work together to maintain (or stop) the loop (notice how i said loop, it uses a loop but isnt a while or for loop, it connects the end of one code and the start of another, like a chain . I made it in the pull request #2, check it out: here"}, {"post_id": 54151, "ts": "2026-08-24T23:51:28Z", "hours": 5.8114, "text": "Fixing some forgoten code and more So at this devlog i were fixing some bugs in a specific ui / route called ¨copa¨, this is a special route added because a teacher asked our team to create them to use at some football games in our school. But since it was a hiddend route and mostly like a easter egg it hasnt been updated in a while and wasnt working anymore. What i fixed i was following a pattern where most of the images that each route used should be in a special foulder of storage (literally called /storage), but this mode (copa) where created before it and was following the old way Also there was some errors ocurred afther i changed the entire folder structure, so the copa mode where completely broken, i fixed it too. What i added I had a class about PHP + Laravel | API | Controlers in my course, afther that class i realized that the way the API (copa) where searching for data based on ID where wrong (was doing copa?id=…), i changed it to /copa/id There is a function that show the teams to the user to select one that he wants, but the teams list where hard coded and wasnt synced with the database. I made it use the database data instead of a hardcoded value ** That where all for that mode, but i didnt spent 5 hours just on it, i made more: ** Fixing (or making) some other bugs There is a script that controls the database that where holding and infinite single connection with the database, something like this conn = mysql . connector . connect ( ** configs ) cursor = conn . cursor ( Dictionary = True ) But all the codes where using the same connection and cursor, so i decided to apply a connecton pool and make each database call get a connection and a new cursor. Also i added some functions to abstrac the connection and query part: def __get_connection ( self ): conn = self . db_pool . get_connection () conn . ping ( reconnect = True , attempts = 3 , delay = 1 ) return conn def __execute_query ( self , query : str , params : list | None = None , dictionary = True , query_type : QueryTypes = QueryTypes . ALL ): conn = self . __get_connection () try : with conn . cursor ( dictionary = dictionary ) as cursor : cursor . execute ( query , ( params ) or ()) match query_type : case QueryTypes . ALL : return cursor . fetchall () case QueryTypes . ONE : return cursor . fetchone () case QueryTypes . EXECUTE : cursor . commit () return cursor . lastrowid or cursor . rowcount case _ : raise ValueError ( f \" Tipo de consulta não suportado: { query_type } \" ) If yall want to see it check here at my repo That was also the first time that i used a Enum (on code) and branchs (on github, but i didnt used them properly) Others There was also some other changes, check the repo GF-Silva/Ritualia What next Now i will add some bug checks on a piece of code that handle the video display using an youtube iframe."}, {"post_id": 52754, "ts": "2026-08-23T03:00:03Z", "hours": 4.8206, "text": "Changing some values to be relative I spent a while (more than i expected) changing some strange values that control some settings of a component called cover-flow (this component create and control the images that are displayed in the home screen). I spent more time figuring out what where each variable and lhe logic behind than acually writing.Before it had some hardcoded values that would probably broke if anything changes, i saw it and solved. I wasnt here for it, i was fixing another bug and fixed it before it broke. I also used some basic math of circles like angle of an arc. That is all for today"}, {"post_id": 52465, "ts": "2026-08-22T16:18:22Z", "hours": 3.1097, "text": "I spent some time correcting errors and logic problems, here they are: Error Correction Music playback would freeze when the music queue ended : This problem would break the app until you restarted it, but it was very simple to solve. I had simply made it remove the first instance of music from the HTML twice, which caused it to try to remove an element that didn’t exist, thus freezing the app. Removed an unnecessary loop : In the player section, there was a loop that iterated through all the elements (emotions) in a list to toggle the state of ONLY 1 ELEMENT. With the few elements it had (there were 3), this was minimal, but adding more could be detrimental to functionality. It only did this to toggle the state of the PREVIOUS element from active to inactive. I solved this simply by making it save the index of the last element and use that to access it. It took a while to work because javascript was treating 0 (the first indice / old indice when u start) as false in the if, but afther i figured out why it was simple to solve (if oldIndice != null) Implementations / Changes I separated the API/WEB routes : Previously, the routes exposed by the backend (such as /songs) were in the same file. This wasn’t a problem before, but with the addition of new functions, it could become difficult to maintain and prone to various errors, as the routes weren’t separated (a name could be the same). Therefore, I separated them into: /api (backend API codes) and / (frontend web codes). I put the genres in the database : Previously, the genres were in the frontend, and to add a new one, I would have to update both the backend and the frontend. To solve this, I simply added a route that retrieves the current genres (from the database) and uses the data (name / location) to load them into the frontend. I added the emotions to the database : The emotions had the same problem; although both (they and the genres) were in the database, the frontend wasn’t based on that data, so I did the same thing I did with the genres. I changed the name of where the music explanations were saved : I must have mentioned in a devlog that the folder inside storage was called /explication_source, and I also mentioned that the name was incorrect (because the audio source wasn’t there, nor would it be; the audio files themselves are there), so I changed it to /explication_audios"}, {"post_id": 48602, "ts": "2026-08-17T02:01:56Z", "hours": 3.6708, "text": "Updating the README I saw a post about how to structure a README, how to make it clear and etc. Afther i realized how wrong the old README was (it was confusing, and also was months out of date). So I chose some topics that would be interesting to include in and filled in a skeleton, spending some time learning what to do and what not to do. I also didn’t fill in some fields because they aren’t ready yet (they now are stored in my memory to add later). How it was before: https://github.com/GF-Silva/RitualIa/blob/9a2a7756c777a7042eca28309924e5a408bd561f/README.md How it is now: https://github.com/GF-Silva/RitualIa/blob/2dea5f80266f06e8633af1aa3c9430065cde9231/README.md There is a huge difference. Also i did some bugs and noticed how bad my commits are"}, {"post_id": 47776, "ts": "2026-08-15T22:55:35Z", "hours": 32.5314, "text": "After a development pause, here are the various changes made. Also, I never mentioned it, but this project was made by students focused on the school environment. (This time I finally got around to writing the devlog myself): Structure Moved main.py to the root Replaced mysql-connector with the official version : The added library was a version I didn’t recognize; I added the official one instead. Cleanup Removed some Code that wasnt being used. Backend Change the return value of the SQL cursor : Previously, the cursor returned an array, but for code readability and security, I opted to change it to an array. Structured Error 404 : The backend used abort in certain parts, but it returns HTML and certain parts of the frontend expected JSON, so I formatted the abort to JSON. This caused a headache, I hadn’t tested it (again lol) and when I saw it, it was time to present the project at school and I was having this error. frontend Fixed the CoverFlow component : An error went unnoticed in the creation of the component, this made the size and spacing of the images look strange, it was fixed to a certain extent. I implemented a new playback policy : The text about it got so long that I decided to open an extra topic. Playback Policy Now it search for the requested emotion + genre that the user choose, If It fails It search Just for the genre, if It fails again it search for anything. Now it falls into a fallback: This policy change was motivated by having to find a song to show in a presentation and failing. I reflected that if it was annoying for me, it would be for the end user (students) as well. Database Added new songs : I added 50 extras and their respective genres/emotions. (Also i wrote some helpers in python to help me categorize and search for the musics in youtube and get and structured sql output, that took quite a while) Saved the songs locally : Topic local saving Local saving But why would saving the songs in the cloud be a problem? Nothing comes for free, the server we were using to host the music had a monthly access limit, which was a silent problem that, thankfully, was noticed before it happened. My mistake, I didn’t even think about the possibility of saving locally and went straight to saving in the cloud, it must have been due to haste and inattention (always plan well what you are going to do before you do it, weigh the risks and benefits). Now, how it works The audios are saved inside the /storage folder. The database still saves the location, but this time it saves the relative path (e.g., /explication_audiis/fun_audio.extension). Why save the folder path as well? Won’t the audios always be there? Yes, but at some point the path might change (e.g., explication_source/youtube/audio_legal.extension). Future plans I’m going to improve my devlogs lol, this one got huge"}, {"post_id": 12818, "ts": "2026-06-20T23:46:13Z", "hours": 5.5386, "text": "RitualIa in Evolution: Refactoring, Optimization, and New Features 🎶 Hey everyone! I’m back with a cool update on what’s been happening here at RitualIa. After that commit where we transformed some scripts into helpers, things have evolved quite a bit. We’ve been doing a heavy refactoring of the project, focusing on organization, performance, and better code architecture . What’s changed: Structure & Organization: I removed legacy folders that weren’t being used, did a mega merge bringing everything from /frontend (the folder that where holding ALL the frontend logics) to the project root (with that the frontend and backend are in the same folder), and switched from FastAPI to Flask on the backend. Why the change of FastAPI to Flask: Because my supervisor or mentor (something like this) said and said again to, because there was other guys in the project (still is, but im the main developer, the others stopped doing anything like mounths Ago, and even while they where doing something i still was the main dev), these other guys where total starters and could not hold the learning curve of FastAPI (even with the fact that was Just me that where using It, because it was at the backend and i was the only developing at back). Code Quality: We made several methods private in the PlayerControls class, making the API cleaner and more secure. We also fixed the “enclosing class errors” that appeared. These small fixes make a huge difference in code reliability. UI/UX Improvements: We created a new CoverFlow component to replace GenreCylinder on the homepage – it’s much smoother and prettier now. We added support for async onClick, grab + touch in CoverFlow, and adjusted visual positioning to keep everything aligned. Oh, and we removed that unnecessary playerText that was cluttering the interface. New Features: A really cool feature that was added was the checking for duplicate songs in the queue . Nothing worse than accidentally playing the same song twice, right? Now that doesn’t happen anymore. Maintenance & Updates: We updated the fetches to call the new routes, moved the favicon to assets (because everything has its place), and focused on the details. What’s coming next: We are in a consolidation phase, laying a solid foundation for the upcoming features. The focus now is on ensuring that each component is doing exactly what it needs to do, nothing more, nothing less. Thanks for following along!"}, {"post_id": 11702, "ts": "2026-06-19T02:03:07Z", "hours": 1.0236, "text": "I’m transforming some duplicate code into helpers so they can be more easily used in new code."}, {"post_id": 11412, "ts": "2026-06-18T17:53:37Z", "hours": 1.2881, "text": "Introducing: The ‘Ritualia’ project is a project developed by me and some colleagues with the help of the school. In it, we have done and continue to do research and assembly with the aim of impacting the daily lives of students in our school and (in the future) other schools. It is an open-source project with an article soon to be published detailing the foundations used, etc. The readme is currently outdated because I am redoing part of the current architecture, but I will update it later to include information on how to assemble and create your own version. The project is divided into hardware and software. I, along with other members, am responsible for the software (which is usable on most computers), and the hardware is relatively simple (it only depends on a display + sound + a controller with an OS + the preparations for installation)."}]...
|
Title: Ritualia by @gabriel_ferreira | Stardance
Title: Ritualia by @gabriel_ferreira | Stardance
Devlogs:
Introducing: The ‘Ritualia’ project is a project developed by me and some colleagues with the help of the school. In it, we have done and continue to do research and assembly with the aim of impacting the daily lives of students in our school and (in the future) other schools. It is an open-source project with an article soon to be published detailing the foundations used, etc. The readme is currently outdated because I am redoing part of the current architecture, but I will update it later to include information on how to assemble and create your own version. The project is divided into hardware and software. I, along with other members, am responsible for the software (which is usable on most computers), and the hardware is relatively simple (it only depends on a display + sound + a controller with an OS + the preparations for installation).
I’m transforming some duplicate code into helpers so they can be more easily used in new code.
RitualIa in Evolution: Refactoring, Optimization, and New Features 🎶 Hey everyone! I’m back with a cool update on what’s been happening here at RitualIa. After that commit where we transformed some scripts into helpers, things have evolved quite a bit. We’ve been doing a heavy refactoring of the project, focusing on organization, performance, and better code architecture . What’s changed: Structure & Organization: I removed legacy folders that weren’t being used, did a mega merge bringing everything from /frontend (the folder that where holding ALL the frontend logics) to the project root (with that the frontend and backend are in the same folder), and switched from FastAPI to Flask on the backend. Why the change of FastAPI to Flask: Because my supervisor or mentor (something like this) said and said again to, because there was other guys in the project (still is, but im the main developer, the others stopped doing anything like mounths Ago, and even while they where doing something i still was the main dev), these other guys where total starters and could not hold the learning curve of FastAPI (even with the fact that was Just me that where using It, because it was at the backend and i was the only developing at back). Code Quality: We made several methods private in the PlayerControls class, making the API cleaner and more secure. We also fixed the “enclosing class errors” that appeared. These small fixes make a huge difference in code reliability. UI/UX Improvements: We created a new CoverFlow component to replace GenreCylinder on the homepage – it’s much smoother and prettier now. We added support for async onClick, grab + touch in CoverFlow, and adjusted visual positioning to keep everything aligned. Oh, and we removed that unnecessary playerText that was cluttering the interface. New Features: A really cool feature that was added was the checking for duplicate songs in the queue . Nothing worse than accidentally playing the same song twice, right? Now that doesn’t happen anymore. Maintenance & Updates: We updated the fetches to call the new routes, moved the favicon to assets (because everything has its place), and focused on the details. What’s coming next: We are in a consolidation phase, laying a solid foundation for the upcoming features. The focus now is on ensuring that each component is doing exactly what it needs to do, nothing more, nothing less. Thanks for following along!
After a development pause, here are the various changes made. Also, I never mentioned it, but this project was made by students focused on the school environment. (This time I finally got around to writing the devlog myself): Structure Moved main.py to the root Replaced mysql-connector with the official version : The added library was a version I didn’t recognize; I added the official one instead. Cleanup Removed some Code that wasnt being used. Backend Change the return value of the SQL cursor : Previously, the cursor returned an array, but for code readability and security, I opted to change it to an array. Structured Error 404 : The backend used abort in certain parts, but it returns HTML and certain parts of the frontend expected JSON, so I formatted the abort to JSON. This caused a headache, I hadn’t tested it (again lol) and when I saw it, it was time to present the project at school and I was having this error. frontend Fixed the CoverFlow component : An error went unnoticed in the creation of the component, this made the size and spacing of the images look strange, it was fixed to a certain extent. I implemented a new playback policy : The text about it got so long that I decided to open an extra topic. Playback Policy Now it search for the requested emotion + genre that the user choose, If It fails It search Just for the genre, if It fails again it search for anything. Now it falls into a fallback: This policy change was motivated by having to find a song to show in a presentation and failing. I reflected that if it was annoying for me, it would be for the end user (students) as well. Database Added new songs : I added 50 extras and their respective genres/emotions. (Also i wrote some helpers in python to help me categorize and search for the musics in youtube and get and structured sql output, that took quite a while) Saved the songs locally : Topic local saving Local saving But why would saving the songs in the cloud be a problem? Nothing comes for free, the server we were using to host the music had a monthly access limit, which was a silent problem that, thankfully, was noticed before it happened. My mistake, I didn’t even think about the possibility of saving locally and went straight to saving in the cloud, it must have been due to haste and inattention (always plan well what you are going to do before you do it, weigh the risks and benefits). Now, how it works The audios are saved inside the /storage folder. The database still saves the location, but this time it saves the relative path (e.g., /explication_audiis/fun_audio.extension). Why save the folder path as well? Won’t the audios always be there? Yes, but at some point the path might change (e.g., explication_source/youtube/audio_legal.extension). Future plans I’m going to improve my devlogs lol, this one got huge
Updating the README I saw a post about how to structure a README, how to make it clear and etc. Afther i realized how wrong the old README was (it was confusing, and also was months out of date). So I chose some topics that would be interesting to include in and filled in a skeleton, spending some time learning what to do and what not to do. I also didn’t fill in some fields because they aren’t ready yet (they now are stored in my memory to add later). How it was before: https://github.com/GF-Silva/RitualIa/blob/9a2a7756c777a7042eca28309924e5a408bd561f/README.md How it is now: https://github.com/GF-Silva/RitualIa/blob/2dea5f80266f06e8633af1aa3c9430065cde9231/README.md There is a huge difference. Also i did some bugs and noticed how bad my commits are
I spent some time correcting errors and logic problems, here they are: Error Correction Music playback would freeze when the music queue ended : This problem would break the app until you restarted it, but it was very simple to solve. I had simply made it remove the first instance of music from the HTML twice, which caused it to try to remove an element that didn’t exist, thus freezing the app. Removed an unnecessary loop : In the player section, there was a loop that iterated through all the elements (emotions) in a list to toggle the state of ONLY 1 ELEMENT. With the few elements it had (there were 3), this was minimal, but adding more could be detrimental to functionality. It only did this to toggle the state of the PREVIOUS element from active to inactive. I solved this simply by making it save the index of the last element and use that to access it. It took a while to work because javascript was treating 0 (the first indice / old indice when u start) as false in the if, but afther i figured out why it was simple to solve (if oldIndice != null) Implementations / Changes I separated the API/WEB routes : Previously, the routes exposed by the backend (such as /songs) were in the same file. This wasn’t a problem before, but with the addition of new functions, it could become difficult to maintain and prone to various errors, as the routes weren’t separated (a name could be the same). Therefore, I separated them into: /api (backend API codes) and / (frontend web codes). I put the genres in the database : Previously, the genres were in the frontend, and to add a new one, I would have to update both the backend and the frontend. To solve this, I simply added a route that retrieves the current genres (from the database) and uses the data (name / location) to load them into the frontend. I added the emotions to the database : The emotions had the same problem; although both (they and the genres) were in the database, the frontend wasn’t based on that data, so I did the same thing I did with the genres. I changed the name of where the music explanations were saved : I must have mentioned in a devlog that the folder inside storage was called /explication_source, and I also mentioned that the name was incorrect (because the audio source wasn’t there, nor would it be; the audio files themselves are there), so I changed it to /explication_audios
Changing some values to be relative I spent a while (more than i expected) changing some strange values that control some settings of a component called cover-flow (this component create and control the images that are displayed in the home screen). I spent more time figuring out what where each variable and lhe logic behind than acually writing.Before it had some hardcoded values that would probably broke if anything changes, i saw it and solved. I wasnt here for it, i was fixing another bug and fixed it before it broke. I also used some basic math of circles like angle of an arc. That is all for today
Fixing some forgoten code and more So at this devlog i were fixing some bugs in a specific ui / route called ¨copa¨, this is a special route added because a teacher asked our team to create them to use at some football games in our school. But since it was a hiddend route and mostly like a easter egg it hasnt been updated in a while and wasnt working anymore. What i fixed i was following a pattern where most of the images that each route used should be in a special foulder of storage (literally called /storage), but this mode (copa) where created before it and was following the old way Also there was some errors ocurred afther i changed the entire folder structure, so the copa mode where completely broken, i fixed it too. What i added I had a class about PHP + Laravel | API | Controlers in my course, afther that class i realized that the way the API (copa) where searching for data based on ID where wrong (was doing copa?id=…), i changed it to /copa/id There is a function that show the teams to the user to select one that he wants, but the teams list where hard coded and wasnt synced with the database. I made it use the database data instead of a hardcoded value ** That where all for that mode, but i didnt spent 5 hours just on it, i made more: ** Fixing (or making) some other bugs There is a script that controls the database that where holding and infinite single connection with the database, something like this conn = mysql . connector . connect ( ** configs ) cursor = conn . cursor ( Dictionary = True ) But all the codes where using the same connection and cursor, so i decided to apply a connecton pool and make each database call get a connection and a new cursor. Also i added some functions to abstrac the connection and query part: def __get_connection ( self ): conn = self . db_pool . get_connection () conn . ping ( reconnect = True , attempts = 3 , delay = 1 ) return conn def __execute_query ( self , query : str , params : list | None = None , dictionary = True , query_type : QueryTypes = QueryTypes . ALL ): conn = self . __get_connection () try : with conn . cursor ( dictionary = dictionary ) as cursor : cursor . execute ( query , ( params ) or ()) match query_type : case QueryTypes . ALL : return cursor . fetchall () case QueryTypes . ONE : return cursor . fetchone () case QueryTypes . EXECUTE : cursor . commit () return cursor . lastrowid or cursor . rowcount case _ : raise ValueError ( f " Tipo de consulta não suportado: { query_type } " ) If yall want to see it check here at my repo That was also the first time that i used a Enum (on code) and branchs (on github, but i didnt used them properly) Others There was also some other changes, check the repo GF-Silva/Ritualia What next Now i will add some bug checks on a piece of code that handle the video display using an youtube iframe.
Optimizating some piece of code So there was a certain part of the code responsible for the songs queue in the main route, that piece is a core function but where running a while true loop for no reason , here is the part: async start () { while ( true ) { // { emotion, genre, author, name, sourceId } const musicData = await this . #musicQueue . get (); this . createPlayer ( musicData [ ' sourceId ' ], null ); this . #showMusicInfos ( musicData [ ' name ' ], musicData [ ' author ' ]); this . #queueList . removeChild ( this . #queueList . children [ 0 ]); await this . #startExplication ( `/storage/ ${ musicData [ ' explicationSource ' ]} ` ); this . player . playVideo (); await this . #musicFinished . when ( true ); this . #musicFinished . set ( false ); } } Basically the code were calling it at the start and running it forever, but that code where a nightmare to maintain / implement, i tried to implement a safe check here for over 2 hours, but wasnt possible. I replaced it for a more efficient approach that do a loop in a different way, here it is: when adding a music, it checks if it is already playing, if no starts to play. when start to play it checks if there is a music in the queue for it to play, if no it stops playing, if yes it get it and play. When the music that is playing ends, it call the play function (from the above topic). That is just it, no while or constant loops, no overhead, it works by basically connecting all the codes in a way that they work together to maintain (or stop) the loop (notice how i said loop, it uses a loop but isnt a while or for loop, it connects the end of one code and the start of another, like a chain . I made it in the pull request #2, check it out: here
Fixing errors and adding bug checks Hi everyone, so i were fixing some bugs that i saw that may broke the app, i fixed all of them (for now), here they are: There was a logic bug that a class (the one that controls the youtube iframe) where doing stuff that it wasnt supposed to do (not an unexpected behavior, but a logic that should be outside of it), it was an easy fix. There was a bug at the player class that when the provided sourceId (for the youtube iframe) where wrong or unable to use it crash and stopped doing anything else (like playing another musics). I tryied solving it in various ways, one was beffore the changes that i did in my last devlog, but i failed because the class that holds the player where hard to fix, this devlog fix it. I fixed it by adding some bug checks and, when some error ocurred, it play the next music. There was also a bug at the player class and youtube-iframe-controls class, that for a couple seconds in the start of the website it would broke if u tried to play a music. It was because in the start of the program the youtube API where unable to be used. To fix it i just made it wait until the API where ready using an ASYNC function. There was also a bug that, afther the past devlog, it was doing and request at the /api/songs with params at the wrong format. Afther the past devlog the API started using the params genre_id and emotion_id , but the frontend where passing with the incorrect name (genre, emotion). Something that i implemented I upgraded the mysql query that where being used to get more infos about the music, before it where just returning infos like name, author, sourceId, etc, but now it also returns the genre and emotion from that given music. I added a bug check at the player class, now if it fails it pass to the next music. Something that i want implement I want to implement a simple toast notification system that will apear when something fails, like the playing of some music. Others The youtube docs for the iframe where very helpful for this changes, check it out: https://developers.google.com/youtube/iframe_api_reference?hl=pt-br
Creating a toast notification system So rn im creating what i said in the past devlog about a toast navigation. This isnt finished yet, im still figuring out an way to check the end of the timer, i found some ways to do it, like using an simple if inside the countdown. I didnt comment but i am using an setInterval to decrease the time and an <progress> tag to show the value but that is a bad ideia, imagine doing 1 if every 50 ms? that dont cost a lot of processing power, but i still will search for other ways to do it just to check, if yall got any hints or ideas leave a comment. Also i will improve the design, the design is petty poor.
The toast notification is finished So i finished it, it is a component that u adds in the code that u wants to use. I added 2 icons, 1 for infos and another for warnings. What i used and did The element that holds it all is an dialog , the icons are svg , the text is a span and the element that shows the time left is an progress . To update the time left i used an setInterval that runs at a 10ms delay and deduce 0.01 of the dialog value, so it will be smooth to see. Also there is an if inside that destroy the element and clear the interval when the value of the dialog reaches zero or less (In the past devlog i said about not using an if, but considering how low the cost for an if in that time is i decided to use it anyway). The function that creates the toast receives some params that will let it be customized by whoever is calling it: const icons = Object . freeze ({ INFO : infoIcon (), WARNING : warningIcon () }); function createToast ({ message , time = 5 , styles , icon = icons . INFO }) // the `message` is the displayed message // the `time` is the time until close // the `styles` is custom css styles for the element, it uses the dictionary format {} // the `icon` is the svg icon What else? I will apply it in some elements and post about. See you later!
Toast applied and tested The toast notification component is now being used at the player class, i did some tests and it is working very nice, hopefully the user will not see it very often (because that would be a problem, the user must not see it unless there is an error playing the music). But why did i make it? It is used to show more about what is going on for the user, so they will not become anxious if it something is working or not. Why not use another notification system? Each notification system has its own use cases, the others would be very painful for the user. Imagine every time a simple error (that is simple for me) like the music fail to play there was an error that interrupt the use of the app with an popup or alert, that would be painful, dont it? That is why i used an toast notification, because it is temporary (disappears after some seconds) and dont stop the use of the website. Also there is a feature that i implemented for the same reason a few devlogs ago that add a fallback in the music search, so it wont show a message at every single combination. Lets calculate how many combinations we have: there is around 50 musics in the database, 22 genres and around 40 emotions, multiplying the genres and the music 880 combinations that the user can make. Now lets see how many musics we can get: each music have around 1.2 emotions and 1.2 genres, so we get 72 combinations that lead to an music . With that in mind, we can see that from 880 combinations, just 72 of them are useful combinations, so we get a 8.18% for getting a music, and 91,12% of not getting one. Consider that each failed attempt shows an error that stop the use flow of the website, but that was before the past devlog, now it tries again and again and only afther failing everything it shows an error, at that point it must really show an error. Thats all, sorry for the long boring text and thanks everyone that read, see yall later
Studing about cache So basically i where studing about cache, the main ideia was to implement it in the sistem, but then i realized something: Theres is nearly no difference between cache and a normal request in an localhost project, adding the cache would not improve anything, sometimes it maybe even slow the website, because the page would need load more code. I realized it afther a while studing about it, but anyway lets focus on another thing. Also i where studing with google for devs: https://developer.chrome.com/docs/workbox/caching-strategies-overview
Adding a new description feature So basically there was an place where was designed to have the description about an music, but that was empty and where only finished today What it do It is petty simple, it just show an text about the music that is currently playing, also there is an audio that plays before the music starts playing, but this audios is played just one time before the music so if someone loses it or didnt listen well there was not a second chance to listen for that audio. Now there is this place that holds the same content, but in the format of an text that can be read whenever they want while the music is being played, that was what i added. Also that text can be hidden, so it will not stay using half of the screen every time (in the screenshot it is active) What this audios do This audio / text is part of the main purpose of the website, they give some fun facts about the music that may be useful someday in some kind of quiz or essay. Also the audio is played before the music and the music plays just after the audio finishes. That is to make whoever is listening listen to the entire audio before having what he (or she) wants, so they will not listen to the music and skip the audio.
Adding more bug checks I added some checks in the player class at the startExplication function that checks if the src (the url for the audio) is valid or not by doing an fetch with the HEAD method and checking if the return is an valid audio before trying anything in this function. Also i solved this error in the same function: Uncaught (in promise) AbortError: The play() request was interrupted because the media was removed from the document. https://goo.gl/LdLk22 This error was because the startExplication function where trying to play the audio before it was loaded, i solved it by making it play the audio just after it was load with the event listener canplaythrough . There was another error at the playMusic function where the code where trying to play the youtube video before it was loaded, i solved it by making it wait until it was CUED (Where the video isnt playing, but is loaded and showed). (The website was not created to be in mobile and dont has a lot of support, the video where recorded in my smartphone because i had to record the audio and the audio on my pc dont work. In the Future i Will add mobile suport, but right now im finishing the bugs and etc)
Refactoring the copa script I where placing some scripts in a class. These scripts are related with the player functions of the copa script, so i pick all of them and placed in the same class. I did it to follow best practices, but i confess that i still got some doubts about when to create an class and when the variable or method should be static or private (#property), i will search and learn more about because maybe i will have to refactor some parts of the code following these rules of best practices. That was all for now, see ya!
Creating a new helper Context: There was an class called youtubeFrameControls , this class basically where designed as an structure to help using the youtube iframe API, but with the time this class where becoming basically to hold just an function (and in a very inefficient way), because the methods that it had before (like the one that runs when the iframe is loaded) had to have more and more specific logic in the codes that used it, so they where useless (because the codes that where using it where rewriting it). What i did: I picked the only function that where being used and transformed it in an helpers (that can easily be customized). This function was the one that creates the player of the musics, and the function where called createPlayer . Also i removed the old class (youtubeFrameControls) and replaced it for this helper in every place that used it. It now is way more simple than before and can me more customized.
Making the website responsive Hi again! Like i said in some devlog, the website dont have support for mobile, that was because it was designed to be at kiosk mode and work just in desktops. but i decided to change that. First of all: the site where using a lot of absolute values of dimensions, so it where braking even in the desktops (it wasnt responsive at all). But i spent a while changing it, it is isnt ready and has some problems, but is better than before. (The mobile is still strange, i did not started fixing it yet). Credits: https://css-tricks.com/snippets/css/fluid-typography/ - This website shows about a way to make the texts become responsive for the screen using some calcs, it is worth seeing. The first video is how it was before, the second is how it is now
Hi again! Like said in the past devlog, im improving the responsiveness of the website, specifically in the player part. But it had some problems and where distorting when playing an video, now it resizes based on the screen size in a way that it stays ¨normal¨. The first video is the new way, the second is the old way. It still needs a lot of improvements, but it is going!
Im improving the responsiveness at the home page (before was the player page), but for some reason that idk the emotion selection div is having a strange behavior, i will update what it is after i fix it. Take a look: (the item in the center should be centered)
i finished the workings on the section of the home page that i where doing before, now has a good responsiveness for pc and a decent one for mobile. Now im working on the main area of the home page, the one where the user selects the desired genre for the music (second img). It may looks completely fine, but that same element already was a headache before and is here again. The given area is the cover-flow component, before i already fixed it (or so i thought), but it was looking bad (the 3d perspective where a bit strange) and would probably broken if i added new genres. I were trying to figure out a way to listen to the clicks in the card without any problems, that seems easy at first glance, but the 3d perspective in the element make other elements eat the click from one another, make the clicks dont be listened and so many problems that i nearly decided to erase it and write from scratch. I already found a way to do it by using the mouse position, the center of the screen and the card width (the genre img) to transform it in an value that can be used to select the exact card without any dependencies on the doom selector. It basically uses just some values, put in some calcs and get the difference between the current card (genre) index and the clicked. Im nearly finishing it, but i still need to figure out some values and more.
Finished the correction of the errors at the perspective and click in the cover-flow component. Now the perspective stays the same 150vw value (that is based on the screen width) and stays normal, with everything working. Oh man, that needed a lot of logic and out of box thinking finding another ways to check where the user clicked without using an normal click event listener (for those who lost the past devlog or didnt understood, i cannot use a normal eventListener in that because the clicks is detected in another element or in nothing, even with z-index or more in css, so the solution should be done entirely on javascript logic). The first video is the new way, the second is how it was before all changes in it
Im nearly finishing the workings on the responsiveness. Now the entire player screen is responsive and do not breaks the texts anymore (except for the description area), also the buttons that show you the next screen (the ones that displays ‘player’ and ‘generos’) are responsive too, the music queue is working perfectly fine, and the area that is used to select an emotion is nearly responsive too, it just needs some adjusts in the two buttons that change the current emotion because they become too small and uses a lot of space when the screen is too small, after that this screen will be finished. It is taking a really long time, the fact of how many different screen sizes exists makes it harder to finish, but now there is times that i even cannot see if i changed the resolution in some areas, because the system looks the same. I spent quite a while testing different resolutions and combinations of size, i didnt even noticed that i where 6 hours here, but now ik more about css (things like container-type + container based sizes and more ways to set the size of an text) The video shows how it is now, if u want to see the before just checks the past devlog.
The emotionDrum is now responsive! Now the buttons that you use to select the emotion are in the same side as the div that displays the emotion, so they dont fight for space anymore. This change is meant to fix the low size of the items in screens that are too small, in the new way we have more free space to display the emotions, there is not much to say. What else I did some minor changes in the size of some items at the player area, now they are easier to read! (I increased the size of some items)
The home is now fully responsive Ah that took a while, but finally it is fully responsive. Now the description at the player UI resizes based on the screen size and active a scroll behaviour when need, also i did some minor change at the emotionDrum perspective to make it show more emotions at the same time, removed the padding in the arrows at the emotionDrum so they will have more space and fixed an error in the genre drum that was making it perspective broke. After all these devlogs i improved how i make a code responsive.
Improving the performance After finishing the improvements in the responsiveness, now i decided to improve the performance. I used lighthouse to get insights (i learned about it in an tiktok video) and figured out that the images size were too high (were an png of 1280x1280px), so i saw more about that in the page: https://developer.chrome.com/docs/performance/insights/image-delivery (an link that google lighthouse gave). With that i saw how to decrease the size while staying with quality, i reached up to the sharp-cli tool, that makes the work of resize, convert and compress the image for me. The command used was that: npx sharp-cli -i * .png -o ./output resize 320 -f avif -q 50 --effort 6 --smartDeblock --preset photo The 320 is the size that i want it to be at the end, i got that value by seeing what was the computed value (the value that is being used in the website) in an high resolution pc (not the most higher one, the high but normal pc) with the resolution of 2500x1714px . With this simple changing the image went from 2mb to 40Kb! Nearly 100% decrease, and take in mind that there is more than one img, so the change were even higher. Also i created 5 different images: 320px, 480px, 640px, 960px e 1280px. With them the browser can choose which one is better for each device. With these changes i went from 35 points of performance to 59.
Improving the LCP (Largest content paint) performance I am improving the way that the website create and downloads the images, the main reason is to make the user see something faster. Before the system were doing 2 main things: Go trough the entire list of images, creating the elements and placing them into the page. Position them. But the position of the items were being done after the creation of the images, in another function. Now they are in the same function: it create the element, position it and add it to the page. That does not seem like much, but the code got way faster with that, probably because it dont need to go item by item in an list twice just to create an element. I tested the time to load the first element and it went from 8.63s to 6.43s , the test was made with 2200 elements (Yeah ik, it is a lot and normally it isnt that much, but i had to see how they were holding with more than 22 items). Also i fixed a bug that passed unnoticed while improving the responsiveness: At some point of images the visibility of the elements broke, that was because the perspective (what makes the 3D perspective of the component, and makes it visible with 3D effects) was based on the screen size, not the elements itself. What was the problem in that: Each element takes a size in the screen ‘makes the circle that shows the element be more and more near of our point of view’ (i cant explain it any better, search what is ‘perspective in an 3d element at HTML’ ). While it was approaching our point of view (POV), our POV were exactly at the same position (based on the screen size), so at some point the circle perspective would pass the POV and we would be unable of seeing it well. Now it is based on the elements: The 3d elements were basically a circle, i just used the diameter to change the perspective based on it. TODO: Maybe i lowered the image quality too much… I will fix it in the next updates. Also i were trying to improve the font settings to improve the performance, but that wast working so i decided to move on to this because it was more critical (took a while for me to jump to this, maybe like 2 hours trying and trying values in the font settings). i am also working in a way to load the images in parallel, with that it will take way less time (I didnt known about LCP before so i spent a while figuring out the way it works).
Improving the loading performance Now the system selects some images that needs to be shown faster than the others and making them load with priority. It was made by selecting the images that the user is seeing and defining some params like fetchPriority = "high" and decoding="sync" , the other images have the params: decoding="async"; loading="lazy"; contentVisibility="auto"; . With that config the LCP went from 2.4s to 0.5s . Also it loads the image in the start and dont change that config again, so the browser will be lazy while downloading these images. The problem is: only the first images are chosen as high priority, but their positions will be changed while the configs stays the same, that makes the browser lazy while loading them and makes the runtime performance became worse. To fix that i found about IntersectionObserver, that are meant to solve the problem of updating the images position fast. It was not applied yet, i found about it like 1 hour ago. What else Now the perspective of the emotionDrum is based on the elements that it have, before it was based just in the screen and would broke if something changes. I did a little change in the way that the script checks for clicks in the screen, now it has more accuracy (I changed the round system).
Adding an observer Now the script observe the card elements at the cover-flow component, classify their position and changes it visibility based on that. Im using an IntersectionObserver to observe every card, but it was designed to observe the position of 2d elements, so i had to figure out how to make it work well in an 3d element. The way it works is when the observe activates (it basically means that some card is in the range to load) it get the card info (like position and angle) and changes it visibility state based on that. Simple, right? No, it was a headache for the start to the top, nearly 4 hours of what i did was to fix the fact that it was meant for 2d. The observer were activating for cards that were in the back of the element, so i had to classify the card position into back or front by using its angle. But if i tried to use the card angle without anything else the script would broke (and did for 3 hours until i realized how to fix), so i had to use an second angle which is 360 - cardAngle (the 360 is the angle of an circle), so this new angle is based on the circle end while the other is based on the circle start. But the system still broke, because at some point one of these values would become negative and makes the if that classify the elements fail. The fix was using the absolute value in the if (it took 1 hour until i figured that; oh i were so happy when it worked). Why To improve the performance, so the browser will just load what is really important and not 50 images that the user cant even see. What else All that work definitely improved a lot my logic thinking and for the first time i used the truth table well. Now i just need to use the new code to change more the style of the images that are not in the screen. I did not deleted the code that i wrote in the past devlog, it was used in the new code to select how many images should be loaded in the same time. ** The first video is the result, the others is some problems that i had **
The observer is finished (nearly) Now the cover-flow has an observer that checks the position of the images and changes it styles based on that, so the images that the user is seeing is loaded while the others is hide. It still has some bugs and space for improvements that im still figuring out how to solve, like the image loading too late on the screen or, in slow internet connections, the image takes too long to load. Most of these bugs are because the observer (an intersectionObserver ) was designed for 2d contexts, not 3d (that were and still is an giant problem to solve). For this reason i was thinking about creating an observer from scratch in C++ that can work better in my 3d element, but the overhead would be high so im trying to solve in other ways before that. The feature was made using fetchPriority, contentVisibility and src (the system add the source just when the image is near the screen). OBS: I found about MutationObserver after this post, maybe i will use that instead of insersectionObserver. Oh man, at least if had found about this before… Aaaaaahhhhh i spent 9 hours in that, but at least a Lot of the new logic will be used. Anyway, lets cook
Adding a mutationObserver Now, instead of an intersectionObserver, the system use a mutationObserver that monitors the angle of the cylinder itself. With that the loading of the images will be insanely better, faster and user-friendly. Now the changes in the cover-flow is observed at the cylinder angle, not at each card. There was some problems in the journey like, for some reason, unexpected cards being loaded or not, the loading script at the observer dont triggering at all (that was caused by the speed of the observer) and an insane amount of cards being triggered at the same time (i still did not figured out why, but that isnt ‘harmful’). But man, i had to be creative here and remember about some concepts like concurrent programming, buffer / queue and apply some delays (because the mutationObserver is insanely fast and can trigger every millisecond). That all was meant to decrease the speed of the mutationObserver, because it was triggering with the most small change in the cylinder, so i made it trigger just when an expected amount of movement was made. The red/yellow collor is for me to visualize the loading state, it is just for debug The code isnt finished, i still need to implement the loading part, for now i just implemented the observer and basics of the loading part
The loading is done Features Now the loading at the mutationObserver is finished, it finally loads or unloads the images that should be (or not) loaded! The new observer is way faster, because it can detect the image way before it enters the screen, loading it before and having more time to do it. Also the system loads just a maximum of images (with the configs that i put it is 13), so it will not fry an cellphone. Problems The loading part was easy to do, i already done it before in the past observer (intersectionObserver), so i just had to pass the old code to the new part. But when the cover-flow element dont got atleast 13 elements the mutationObserver code breaks completely! Im still pondering how to solve it, when i do i will post about! Results The performance went from 78 to 100! The system is way faster! The user does not see a blank image anymore! Why The old method was slow even with a good internet connection, because it was loading the images just when the element was already in the screen, leaving to a perceptive delay for the user and a blank image to the user. Also i thought about creating an observer from scratch (maybe i was a little off my ideas, because that was insane), but the browser already had exactly what i wanted (the mutationObserver) Videos The new features The performance of the new feats The old features Reasoning While i was trying to sleep i thought about a system that fill the cover-flow with a maximum of images (like 10, or 13, or the code decides) at the DOM, using less memory to run. That system will free a lot of memory based on how many cards the cover-flow have. But that will have noticeable effects just with a lot of elements and probably would be hard to develop, maybe i will do in next updates. What next Im leading to the shipping, so i will improve metrics about the lighthouse, improve the readme and post it.
The loading is done and working! Fix I fixed the error that were happening when the cover had a certain amount of elements. Problems The fix itself was very easy to do, but doing it in a readable or without using 10 lines of code (i want things concise) was way harder than i expected. So i spent this entire time trying different ways to do it, each of them making everything worse. Reasoning The bug were with less than 14 elements, the script were calculating the first and last element index, it happened that these elements were behind the screen focus. Why? Because the values that the system was using were leaving these values, so i had to fix the way it was calculating things.
Fixing an error at the youtubeHelper Now the youtubeHelpers checks if the youtube API is ready before sending commands. Why Sometimes the API maybe is not finished yet, that was leaving an untreated error that was braking the entire website. How was made With a lot of pain and suffer. I thought “Oh hey, that will be a 15 minutes fix” - i spent 1 HOUR FIGHTING with the iframe itself, nothing was working and at some point i started thinking that even the API of YOUTUBE wasnt working, even the most basic function that should be triggered when the youtube finished the loading wasnt beeing activated, even the console.log(“Api READY!”) wasnt working, even when it worked it wasnt working well, the variable of the player was a promise and not the result. At the end i shut down my PC, defeated, without any energy left, with just hope that in the next day (this) that would be fixed (if not i was planning to ignore and make it a feature “Easter egg, if u dont join the website for a while it will not load!”. The problem here was just the position were the iframe api was in the HTML, it was being loaded before the helper script, so the helper was not there to check if it the iframe was ready or not, the fix was just move it a little down. Architecture It was just an promise, if the function checks that the iframe isnt available it await for it to be available. The check if it was available use the onYoutubeIframeAPIReady, easy easy (except for all the suffer that i went into).
The performance workings is finished and bug fix Fixes Now the logo z-index is lower, that is because the logo was ‘eating’ the click in more important elements. Also i did a small change at the cover-flow, that change fix the bug of the scroll at the element sudently stoping when at a certain position. The copa script had some old references to images that were leading to bugs, i changed it. Performance I already finished it in the past devlog, i was just checking if there was anything worth improving, but everything is working fine and now is time to improve acessibility. Most of the logged time was me trying to improve more some metrics at the lighthouse, but the work was too much for anything that is already good, so i decided to stop before i started focusing too much in it.
The accessibility workings is done Features Now the system has a new navigation system that allow users to navigate with just the keyboard! (The video was with just the keyboard) Why Why not? It was meant to improve the accessibility for users without mouse, or any another problem and because i was tired of having to use the mouse to debug something. How it was made Again with a lot of pain, the feature itself was easy but i had to fix some errors that went unnoticed, so it took more time. The feature detects when the user press a key and do an action based on wich key was. The accessibility was easy to get 100, but the metrics at lighthouse dont give all what the users will need, i had to think about the website usage and how would be the easiest way for the user. Results The accessibility went from nearly 69 to 100! What next Now i will work on the best practices! Update after the devlog: Nearly every metric is already maxed out lol, i will ship it very soon!
All done, it is time to ship Every metric at lighthouse it nearly perfect, im so exited to ship it!
Added install guides Now there is guides to help the user install for ubuntu, arch and windows. Notice that im a linux user and my pc is weak, so i could not test it on another operational system. Also the guide is in portuguese (all of them), please use your browser built-in translator or any of them to read if u dont understands portuguese I could test it on another linux distro, but i would have to go through all the install steps. Also i cannot try installing windows because it normally wipe out another OS installed on the same disc (i know that it just replaces the bootloader) and using a VM was not possible due to the amount of RAM and the processor that i have.
Configuring an ssh server! I configured an oracle cloud vm with ubuntu to host my website 24/7, that was because nest wasnt working in a while. Im running it at a local IP with http only, i will configure https today and maybe an domain, the IP is http://164.152.55.252 , notice that some browsers may block you from accessing and http website. UPDATE AFTER THE DEVLOG: I put an domain using duck dns: http://ritualia.duckdns.org Hardwork I had absolutely not a single knowledge about oracle cloud vm, so configuring it was an hell. Sometime my internet stopped working so i had to use my smartphone, this lead me to configuring an invalid ssh key and i had do configure everything from scratch, i spent 5 hours configuring everything, but that was ssh and wasnt tracked. But now everything is working fine. Thx oracle This vm is a 24/7 entire OS (Virtual Machine) that has some good free plans, without any cold start. Thx stardance Stardance now will have one more mount!!!!!! I still has roughly 40 hours of deep development to do after this ship, i will love to finish that in stardance!
It is time to ship! Now the system is working perfectly fine running at a vm in oracle cloud with a public domain, and a dns with duckdns at http://ritualia.duckdns.org/ Warning: It is just HTTP!
Added english readme I created an english readme, api and install Guide, so it will be easier to read and more accessible for worl-wide users
Ships:
What did i made I made an complete music play system that will help users to learn while listening to musics! I used youtube, flask, python, mysql, js and hope to make this! What else Solved a lot of painful bugs that were stopping the project from working Finished and made the music queue work Added music description labels Improved a lot the performance Made the website responsive Added a keyboard based navigation Fixed so much errors at the cover-flow that now i forgot how many Added bug checks before the play of each video on the player Created an toast component to show errors or infos Added 54 musics to choose and listen Challenges The most painful code was the player and the cover-flow, they were with a lot of hidden bugs that i had to figure out, feats that i had to make and pain to suffer! Now i figured out that i love and HATE making a website responsive! What im proud of MutationObserver: An observer that loads / hides an image based on its position at the cover-flow, this system took a while and worked very well. Error checks at player: The system can handle the error that happen and dont crash! Keyboard navigation: Everyone can use the keyboard to navigate now! Oracle VM: For the first time i used an ssh and hosted an website in my own machine (oracle, not mine)! How to test Access http://ritualia.duckdns.org/ , when you join and the page loads you will find a screen to choose an genre, select which one you want, after you will see an emotion selector, select the emotion that you wanna feel! You will see the player screen and an audio explaining the music (The audio is in portuguese), after the audio the music will play. Notice: The github and the website are in portuguese, please use your browser built-in translator or any that you want to use the software properly! Follow the installation guides at github if you want to install...
|
1792856941.6754558
|
1792943341.6754558
|
|
44119
|
Riggurly by @infinite_silly | Stardance
|
infinite_silly
|
0
|
150.23
|
0
|
[]
|
[{"post_id": 80938, "ts": "202 [{"post_id": 80938, "ts": "2026-10-19T09:22:13Z", "hours": 8.6244, "text": "Scooby-Doo Hello friends! I just got an email from Stardance saying I have 8+ unlogged hours, so let’s dive in? I Wish That I Could [Fail] I haven’t exactly done much of aything since my last devlog. I’ve written two unit tests, I guess, which do partially (mostly) address the situation with ensuring that ROM state isn’t changed by a bad save, such as with an RTC or loading in a bad save. I covered the case for two MBCs (MBC1 and MBC3) that the number of banks for the save is mismatched with what ROM was loaded (eg. 4 banks on a 1 bank ROM or MBC2 style ram on an MBC3 rom, though I will cover this case in the future). As for where the time came from, it may be that I stared at my TODO list too hard, as I have my TODOs for Riggurly in one emacs split and my TODOs for school in the other, but I have my school.org in a completely different location in my FS, in my org directory ~/org instead of my my Riggurly directory ~/projects/riggurly, but I have no clue. I didn’t even edit either file for more than a total of 5 minutes!!! Anyway, that’s all for now folks! Make sure to keep listening to Jamie Paige and drop @ElffawIThink a follow! Also, the novel is a stupid ad I saw on Royal Road whiul ereading 1% lifesteal"}, {"post_id": 79844, "ts": "2026-10-15T16:31:09Z", "hours": 3.4033, "text": "Failing Tests Hello everybody! We’re back with another devlog! Let’s dive right in so I can got to bed! All Tests Pass A good bit of test writing has happened since the last devlog. The ROM suite has gone from a measly 3 tests to a much nicer 16, though still incomplete. I wrote more tests to test combinations of roms and save sizes that are meant to work, as well as a bunch of those that also test RTC functionality for MBC3. I ended up putting ROM construction into a helper, as well as making a helper for most of the save round trip tests, probably saving a few hundred lines of code. Next tests are probably going to be broken saves, saves that don’t match, etc. etc. to see that ROMs are preserved and such. Algospeak Going back to the RTC, I remade the function that adds a number of seconds to the internal RTC. What I was doing before was calling a function that ticked by ticks with an amount of ticks equal to 1 second FOR EACH SECOND. That’s often hundreds of thousands or millions of function calls, with O(n) time complexity. I was pretty easily able to swap it for an O(1) modulus thingy. Switchie-Shwoo Going back to ROM preservation, I recently realised that it was loading in new timestamp data before checking save validity, which could end up being problematic. I just swapped the order of those two and the problem was solved, though, as I said before, this needs to be test-enforced. That’s it for now folks! See y’all next time!"}, {"post_id": 78970, "ts": "2026-10-12T06:56:19Z", "hours": 2.3525, "text": "sleep(); sleep(); sleep(); allTestsPass() Hello everyone! Let’s dive in so I can get back to work before I go to bed! Testing, Testing, 1, 2, 3 For the last couple hours, I’ve been writing unit tests for the ROM module. I’ve been going on and off with unit tests for a while. Before, I wanted to write unit test for cpu instructions before realising Blarrg made test ROMs for that. Because of that, test ROMs are the primary mode of testing out Riggurly, especially since test ROM developers know much more about specifics, like the halt bug, than I do. However, trest ROMs don’t test things like saving and loading, which is something I CAN write tests for in a reasonable amount of time. A lot of the work I’ve done was just making one helper function and figuring out what I wanted to be covered by it and what I want the tests to do. I might also refactor some repeated stuff into another helper for more efficient code after I finish writing the save round trip unit tests. That’s it for now friends, and don’t forget to listen to Flavor Foley’s new album, Mirror Modulation, which dropped today? on all music streaming platforms"}, {"post_id": 78511, "ts": "2026-10-11T01:38:07Z", "hours": 2.9031, "text": "Little Bonuses Hello Everyone! I have to go walk the dogs, so let’s dive right in! Building Codes Since I want a few more hours to try to see if I can get a Framework 13 and Celeste, I decided it would be good to add all the nicities I did for my last CLI, leading-zero-util (I know, terribly named), namely, man file and completion generation. On lzu, I used a hidden flag for completions and had a build.rs for the man page. However, I found it best to merge all of them into one hidden flag for ease of use. At first I tried a build.rs, but it kinda made dependencies annoying and made either my or lib users’ lives worse. That’s it for now folks! Next I’ll be working on unit tests for ROM stuff."}, {"post_id": 77765, "ts": "2026-10-08T06:16:50Z", "hours": 5.5431, "text": "Much TODO About Nothing Hello everyone! We are finally back after a devlog hiatus due to me having to write essays to apply for my school’s software team! Let’s dive in so I can work on my history homework! Read It and Weep Last time, I talked about, in one title line by accident, how writing documentation and documenting my software’s flaws really exposed what was wrong with it. This update is all about fixing those flaws (mostly) A Multitude of Screams One of the biggest issues with my emulator was the audio. Not in the backend, but the frontend. Due to laziness, I didn’t bother adding mono audio support, which ended up being a 5-minute fix. There was also another glaring issue with the audio: Once it crashed, it wouldn’t rebuild the stream. If you unplugged your headphones, thinking the audio would continue on your other speakers, it would just turn it off and modulate speed with std:🧵 :sleep() . It took me a while to write all the rebuilding code in such a way to maximise user QoL for all users. When did I download you?! Since Riggurly is both a CLI AND a library, it has dependencies that are used only in the CLI that are still a part of the package as a whole. Library users still have to download and compile these dependencies, which can be a hassle and a waste of time. I’ve been meaning to put CLI-only dependencies behind a feature so library users can easily prevent themselves from downloading and compiling a bunch of heavy extra dependencies. This also taught me how to use features, which was good. You’re a Waste of the Air You Breathe! One thing that’s been bugging me for a while is efficiency. My array for the fallback memory bus array in the memory bus struct has always been 0x10000 bytes long (1 mebibyte, how much ram a GB has (kind of)). How reading and writing has worked is that there are match statements you go through to see if you access that array or you access the rom or if you access the vram or something else. The problem is that over 3/4 of that array’s length is useless and shadowed by ROM/CRAM/VRAM. I was wasting 768 kb of heap-memory. I ended up dropping the first 3/4 and just having a smaller array. Unfortunately, some space is still wasted, but it’s not much compared to before. I showed @ElffawIThink and they though it was funny. Also, one of the ROM types comes with 512 HALF-BYTES(nibbles) of RAM, which I had been representing with a [u8,512] with the upper nibbles of EVERY BYTE going unused. I shrank this down to a [u8,256] and use both nibbles for a fish in the sea . Take Me Home Before, I had to put a disclaimer in my README that you couldn’t use ~ as your home directory in your default ROM path in your config.toml because different users have different home directories and I have to use the dirs crate to find that out at runtime. This was a surprisingly long fix, though it was quite simple. Just a few lines of code to strip the ~ and replace it with the already found home directory. That’s all for tonight comrades! Make sure to listen to Jamie Paige, especially Clover! That song is now the first song in my playlist with vocal (sequentially, obviously my playlist has other songs with vocals), overtaking Lemon Boy by Cavetown, it’s just that good. It has also shown itself to be a better daily anthem than Villain by Teniwoha and Schifty Five, the YouTube classic."}, {"post_id": 76331, "ts": "2026-10-03T07:10:40Z", "hours": 2.3758, "text": "Lover Tell Me if You CanHello everyone! Let’s dive in so I can work on improving the user experience!## ManThis devlog is all about README. I have actually never used Markdown before, only org, Emacs’s outliner/md format, because it is more intuitive and Emacs gives you more toys. My style guide is in org, my todo lists are in org; the only things that aren’t are the AGENTS.md I stole from Ghostty and the README.md. I ended up using a combination of the Stardance and Phantom README guides to help me figure out what to put in it and how to do syntax. Working on the README put an emphasis on some flaws in my code:- config path ~ not expanding- my glaring lack of a homebrew tap- mono audio still being unsupported- feature gating on binary-only depsThat’s it for now folks!"}, {"post_id": 75838, "ts": "2026-10-01T09:37:11Z", "hours": 3.2122, "text": "Erroneous Hello everyone! I have a history in-class-writing in 6 hours, so let’s dive right in! Error 404 Once I started adding support for loading ROMs and saves, failure points became possible. I hadn’t previously written errors before, so I just wrote strings and propagated them through with Result< T, Box> and the ? operator, with no regard to API users that may want to recover from a failure. I’ve been putting this off for so long, and was even considering doing this after shipping, but since I have so much time on my hands and the only other big TODOs left are the README.md and the Homebrew tap, I decided, why not? This has been a very interesting process over all, and it even gave me the impetus to do some proper gating on load_rom. Different MBCs only support some cram sizes, but because I wasn’t gating on load, these would fail at runtime with a mismatch. Read All About It Next up is the README. I previously had a mediocre, small, incomplete README in a file formet I forgot crates.io doesn’t have a renderer for, so I figured it would be best to start from scratch in Markdown instead of trying to port it over from Org. I also chnaged the name to Riggurly since that rolls off the tongue more. That’s it for this morning! I also found the bathroom in ENA: Dream BBQ, which was cool. I still need to go inside. Also, make sure to listen to Jamie Paige, as she always boosts my productivity!"}, {"post_id": 75451, "ts": "2026-09-30T04:14:29Z", "hours": 4.1544, "text": "Vendetta Hello everyone! Let’s dive right in so I can work on my math homework so I don’t fail my calc test next week! TUIzers I COMPLETED THE TUIIIIII!!! I added a text entry box for people to manually enter the names of their new saves. I also stylised the highlighting a bit more, and , even though its not in the video, now, the color changes to green when you’re selecting a save. This was a really fun learning experience. I originally wanted to make a TUI because one of the raters on my last project, leading zero util , complained about having to use a simple CLI and told me it wasn’t that good because of the lack of TUI, so this has been kind of a personal vendetta against that person to improve my software and user experience. The Other Interface This project isn’t just a CLI utility. The entire time I’ve been building this, I’ve wanted to enable others to make a frontend for it, whether it be on the web or a gui or some port of it in something. I’ve maintained a careful separation between lib and bin, with the exception that they are the same crate. For library users, dealing with my errors would be impossible since many of them are just strings and they are all just Box. Before throwing this on crates.io, I want to make error enums and stuff so people can do error matching. Tap Tap After completing error enums and stuff, I’m going to throw it on crates.io and make a github release so I can make a Homebrew tap. The last time I made a homebrew tap, I gave the project it’s own separate tap repo, but this time I’ll keep all my taps in a new tap repo on my GitHub instead of Codeberg for users. That’s it for now! I’m excited to make error enums so I can have that skill under my belt as well! Also, I’ve reached the point where shipping would probably let me get a framework 12, which is very exciting for me."}, {"post_id": 74790, "ts": "2026-09-28T08:03:25Z", "hours": 3.5664, "text": "Pick Me Hello everyone! Let’s dive in so I can go feed my gecko and sleep! Seleccionar For the past few hours, I’ve been working on the file picker for selecting a rom and a save file. A lot of it is just learning about how to use Ratatui. Originally, I was going to have the selecting happen all at once, by one function, but I switched it to two calls so that load_rom() and has_battery() can be called to check whether the save menu should even come up. Write-In Candidate The thing is, since with a new save, the file has to be constructed by the emulator and it doesn’t exist. Right now, I’m writing a little text input field to be triggered if the save menu gets close with Esc. This will be pretty short, and won’t require a large change to existing code. After that, I’ll wire it in and test. Next Steps Homebrew Cask Separating frontend from backend crate by crate, so lib users don’t have to deal with frontend only imports."}, {"post_id": 73502, "ts": "2026-09-25T07:49:51Z", "hours": 2.4244, "text": "Ratatouille Hello everyone, we’re back with another devlog! Let’s dive right in so I can hit bed so I can go rafting the day after tomorrow! Compliance Chips I’ve finally finished dealing with compliance! I’m surprised by how many extranneous parentheses I had! I knew I had a lot, but I didn’t know it was this much! I’m so happy that is over so I can get to real work! Tui T. Sutherland The last big feature I wanted to add before shipping was a TUI for selecting ROMs and saves. To do this, I need to use the ratatui crate, which fortunately run off the same crossterm my project already does. So far, all I’ve done is learn about it and write the fn for getting the directories/files with the appropriate endings and sorting them for display use. I’ll see y’all in a couple days!"}, {"post_id": 73040, "ts": "2026-09-24T08:03:54Z", "hours": 1.5953, "text": "Comply-cations Hello everyone!! We’re back with another devlog today. Let’s dive right in so I can start the next step! Parensal Issues As I said in my last devlog, while working on compliance for joypad.rs, I realised that I needed to delete extra parentheses around operators with obvious precedence. After working on that file, I continued down the buffers open in Neovim, also correcting that issue in addition to the others. Because I only caught it midway through, I have to complete a second pass of compliance editing. Fortunately, I have mini.surround installed, so deleting parentheses is really easy. That’s all that really happened. Make sure to listen to the daily dose of Jamie Paige and Vane Lily’s new song!"}, {"post_id": 72583, "ts": "2026-09-23T08:54:29Z", "hours": 1.9347, "text": "You Will Be Terminated Hello everyone! We’re back with another devlog! Let’s dive right in since it is 02:00 over here! Lookin’ So Fab I have contined with my quest to implement my BEAUTIFUL and living style guide. Here’s what I did, since this work is mostly just repetitive and long. gpu.rs and memory_bs.rs publicity compliance. Before today, 4/6 of Gpu’s fields were pub(crate) something they had no business being, so I refactored so everything uses methods. Took a chunk of time but it was very worth it. registers.rs This one was pretty stupid. It had two main issues, publicity and masks. The lackluster and incomplete guide I was following had me write registers.rs with a ton of bitmasks it didn’t need it in a way that was ugly and sad, so I dropped the masks. All of Registers’ field and subfields are pub(crate), which I determined is optimal because it would be a major and massive chang to Cpu, and the registers are purely for the Cpu to use, and are part of the Cpu. rom.rs rom.rs was pretty chill, only a few things here and there. joypad.rs Despite this one taking a matter of minutes, this one is going to cause my so much work. All of its publicity was correct, as well as bit selecting formatting. Turns out I’ve been forgetting to remove parentheses around my files! Simply ridiculous! ANyway, that’s all for now, since I need to go touch bed. So y’all later, and keep listening to Jamie Paige!"}, {"post_id": 72213, "ts": "2026-09-22T13:50:31Z", "hours": 2.2914, "text": "If You Do Not Comply Hello everyone! We’re back with another devlog! I need to go to school soon, so let’s dive right in! Numerics, Literally Since I made the style guide, it was time to start implementing it. So far, I’ve gone through emulator.rs, memory_bus.rs, apu.rs, and cpu.rs. emulator.rs This one just needed doccomment formatting. memory-bus.rs This one needed a few numeric literal changes. I also directly accesses the stat field of gpu, which is unnacceptable, but since I’m doing gpu.rs next, I left it for the time being. apu.rs Despite being the file in which I came up with a chunk of my numeric literal style while writing, it felt like every literal was wrong. cpu.rs This was the second easiest file, despite it being the longest by a wide margin. There was some repetitive numeric literal issues in the methods, but that was it. I also tried to split it into multiple files for length since it is double the size of any other file, but I lowkey failed. Next up is gpu.rs compliance. I’ll see y’all next time!"}, {"post_id": 71902, "ts": "2026-09-21T18:55:47Z", "hours": 4.1017, "text": "Dripmogging Hello developer theme users! It’s been a couple days since my last devlog, since I couldn’t make one last night since my dad thought I had school today, so let’s dive in so I can work on compliance and becoming a peer tutor for math for my school! Bitmasks: origins One of the biggest parts of this project is working with bytes and getting data from bytes. To do this, you have to shift the bits you want to the end and then mask them like (byte >> shift) & bitmask . I feel like I’ve gone through dozens of different styles for bitmask and shift numeric literals, which is where the whole style guide idea came from. Personal Stylist For the past 4 hours, I’ve been perfecting a style guide for my codebase. It’s a beautiful org doc with that sweet sweet table of contents with rules on everything from numeric literals to tests to error enums. There’s not to much to say except the next step is going over all 5000+ lines of code to enforce compliance. I’d like that written up A quick note on org. Many online code hosting services like GitHub and Codeberg support org rendering. Rather unfortunately, crates.io only supports a README.md, and not a README.org, which gives me a big sad, especially since I’ll haveto rewrite my README in markdown (ew), which does not give me the luxury of an automatically generated table of contents. That’s it for now folks, see y’all tonight!"}, {"post_id": 70463, "ts": "2026-09-19T07:33:42Z", "hours": 5.4736, "text": "Scream it Loud and Scream it Proud Hello everyone! We are back with the once again DAILY devlog!!! Let’s dive right in! IT LIIIIIVES!!! This morning, as soon as I came up, I sat my @ down to trouble shoot my audio processing on 4 hours of sleep . Despite adding the manual sleep time reduction, I was still experiencing some crackling, so I found a bug in the channels, solving which didn’t solve anything, but switching timing to be based off of audio when available worked like a charm, giving better and more accuracte speeds and crisp audio, so I was able to show off my COMPLETED EMULATOR CORE to some of my friends (as well as my backpack with Ferris on it). IT DIIIIIES!!! After adding the audio, I had to make sure that it wouldn’t cause the emulator to break, so I added a timeout that shuts off audio if it’s broken for more than 500 ms YES CAAAAAAP Because of how I was translating from u16 audio to f32, I was getting popping, so I implemented a software capacitor to slowly dampen sounds closer to EQUILIBRIUM SWITCH IT UUUUP After audio had all it needed, it was time to work on something I had long anticipated, a lib.rs for people o build their own frontends on top of my emulator. I also added an Emulator wrapper struct around Cpu so that it would be easier to manage pub methods and such without the ~1550 other lines in cpu.rs making things hard. There’s a reason this project isn’t just one big file of 5000 SLoC. I also added some documentation, and I found the best use of Claude Code is checking for spelling errors (it’s like I’m illiterate!) Speaking of SLoC, I cracked 5000 today! This is about 3.5x larger than my last two projects combined! I’ll see y’all next time, and don’t forget to listen to beabadoobee’s new album Pylon and Jamie Paige!"}, {"post_id": 70001, "ts": "2026-09-18T07:59:49Z", "hours": 5.9544, "text": "BLAAALAKKLJHSLJHDKHJALKJHLKGSFG Hello everyone! It seems that the devlogs have become oppositely bidaily — just this once, don’t you worry — than my last two devlogs. Let’s dive right in since we have so much to cover. audio output master race After I finished chan 4, it was time to implement master apu registers and channel mixing and panning. Most of the registers were pretty simple, except the one with the master APU power on/power off, as to implement it I had to gate every write on whether it was on, except the wave RAM. I also did that during my PE class (while exercising, of course), which contributed to the difficulty. Fortunately, I write my code in Neovim, so it really sped things up, including 1-handed editing. With that, the backend was nearly done, and I had reached the point at which I would no longer need Pan Docs or anything of the like, just straight Rust sailing. Thread the Needle In order to get the audio timed correctly, I had to hook up tick_channels() to a channel on which it sends a tuple of u16s (left and right amplitudes) to some receiver. This lets the audio be decoupled from whatever frontend one has. Your Pal, CPAL cpal is a a Rust crate for audio and stuff. Here’s the audio stuff because I can’t think straight it’s so late & I have an integration by parts quiz tomorrow and I don’t want to fail something light b/c I didn’t sleep. fn get_audio_stream ( cpu : & mut Cpu ) -> Result < Option < Stream > , Box < dyn std :: error : : Error >> { if true && let Some ( device ) = cpal :: default_host () .default_output_device () { let supported_stream_config = device .default_output_config () ? ; let stream_config = supported_stream_config .config (); let sample_format = supported_stream_config .sample_format (); let ( sender , receiver ) = std :: sync :: mpsc :: sync_channel :: < ( u16 , u16 ) > ( 5000 ); let error_callback = | _err : cpal :: Error | {}; cpu .set_audio_sample_rate ( stream_config .sample_rate ); cpu .set_audio_sample_sender ( sender ); let stream = match sample_format { cpal :: SampleFormat :: F32 => { let data_callback = make_data_callback :: < f32 > ( receiver , stream_config .channels ); let stream = device .build_output_stream ( stream_config , data_callback , error_callback , None , ) ? ; stream .play () ? ; Some ( stream ) } cpal :: SampleFormat :: I16 => { let data_callback = make_data_callback :: < i16 > ( receiver , stream_config .channels ); let stream = device .build_output_stream ( stream_config , data_callback , error_callback , None , ) ? ; stream .play () ? ; Some ( stream ) } cpal :: SampleFormat :: U16 => { let data_callback = make_data_callback :: < u16 > ( receiver , stream_config .channels ); let stream = device .build_output_stream ( stream_config , data_callback , error_callback , None , ) ? ; stream .play () ? ; Some ( stream ) } _ => Err ( \"Audio output sample format not supported! Try disabling audio in your config with 'audio = false'\" , ) ? , }; return Ok ( stream ); } Ok ( None ) } // makedatacallback not shown due to char limits Position,Velocity, Acceleration, Jerk, Snap, Crackle, and Pop After that, I tested it, but there was some terrible audio crackling. Turns out it was due to oversleeping when trying to time things correctly, though before finding this out, I ended up slightly optimising draw_frame().The artifacts seem to be mostly gone and I will leave this to tomorrow silly. nearly @ 5000 sloc"}, {"post_id": 69022, "ts": "2026-09-16T07:14:24Z", "hours": 1.4908, "text": "Madra Channels Here we are; we’re back again, making this a bidaily devlog?! Let’s dive into the last 1½ hours of development so I can touch bed. Me-Chan Navigator After finishing channel 4, it was time to tackle the most complicated (maybe, but 4 is pretty funky), or at least the weirdest. I thought channel 3 varied it’s pitch based on the info in the wave ram, BUT NOOO, it varies the waveform instead. It let’s the gameboy make more varied sounds rather than just variations of beeps, like saw waves and stuff. Lots of its machinery is reused from the other channels, and it also doesn’t have envelope because you can vary the volume yourself. That’s it for this devlog! See y’all tomorrow as I probably will finish audio tomorrow! I don’t think there is an applicable image for this, so have some xkcds"}, {"post_id": 68979, "ts": "2026-09-16T04:46:04Z", "hours": 3.0544, "text": "4Chan Hello everyone! Welcome back to the Daily Devlog, where we go over the new developments in RustyGameGirl!! Let’s get started with today’s broadcast! White Noise, Brown Noise, Black Noise, Grey Noise, What’s the Difference? After finishing the two pulse channels, 1 and 2, it was time to start a new one. I went with channel 4 since it is the closest to 1 and 2 in terms of structure and simplicity. Channel 4 is a randomised noise channel thingy, for stuff, like gunfire maybe? Here’s the summary from Pan Docs: This channel is used to output white noise, which is done by randomly switching the amplitude between two levels fairly fast. The frequency can be adjusted in order to make the noise appear “harder” (lower frequency) or “softer” (higher frequency). The random function that switches the output level can also be manipulated. Certain settings can cause the wave to be more regular, sounding closer to a pulse than noise. A lot of channel 4’s machinery is the same as 1 and 2’s, like envelope and length, which brings us to our next section. Adaptive Water Body Until today, I have never written a trait in Rust. I’ve used them, of course, especially Default and Drop, but also the num-traits crate from when I was making my fast-factor crate, but I’ve never written one before. I figured now was as good a time as any, especially since all four channels share many similar methods, like the nrxy reads and writes, output (to u8 only), clock_length, tick_period, etc., so I added the three following traits: trait Channel { fn output ( & self ) -> u8 ; fn tick_period ( & mut self , ticks : u32 ); fn clock_length ( & mut self ); fn nrx1_read ( & self ) -> u8 ; fn nrx2_read ( & self ) -> u8 ; fn nrx3_read ( & self ) -> u8 ; fn nrx4_read ( & self ) -> u8 ; fn nrx1_write ( & mut self , byte : u8 ); fn nrx2_write ( & mut self , byte : u8 ); fn nrx3_write ( & mut self , byte : u8 ); fn nrx4_write ( & mut self , byte : u8 ); } trait ChannelWithEnvelope { fn clock_envelope ( & mut self ); } trait ChannelSpecificFeature { fn nrx0_read ( & self ) -> u8 ; fn nrx0_write ( & mut self , byte : u8 ); } Having all the separate impl blocks forced and ordered the same makes the code far more readable, and it’s not like this will slow the code, and it sped up the creation of channel 4 and will likely speed up channel 3’s creation. Notes Rather unfortunately, since I was coding piecemeal today and I was to lazy (probably b/c of the gudetama shirt I’m wearing), I didn’t start listening to music until ater I was done coding, while I was working on homework (English I think? It was the only real homework I had today since I did most of the rest in class), but a Polyphia song just popped up in my shuffle (Reverie), and I locked in so much harder. Also, once I started working on the APU, I cracked 4000 SLoC!!! That’s more than my last two projects doubled and combined. It’s also likely the majority of code I’ve written in the 7 years I’ve been programming. I am currently at 4509 SLoC, which is over triple my last two projects combined! See y’all tomorrow, if not later tonight!"}, {"post_id": 68505, "ts": "2026-09-15T07:22:17Z", "hours": 2.2572, "text": "Sweep It Under the Rug Hello humans! We’re back with the DAILY DEVLOG!!!!! I need to go to bed and feed my geck, so let’s dive right in! No Sweeping for You! Today, I finished up last night’s work of finishing channel 2. All that was left was adding an output to u8 fn that’s just sitting there as dead code rn and wiring it up to read_byte(). relatively uneventful, except for when I forgot how bitwise operations work. WeeeWoooWeeeWooo After channel 2, the obvious next step was channel 1. Channel 1 is just channel 2 with an extra feature: sweep. Sweep is kinda funky. I thought it changes envelope, but it actually changes pitch. It does some weird funky shenanigans to change the pitch and also on note trigger. A lot of building channel 1 was just making echo fns for proper layering of structs. Pan docs was once again confusing. Notes A side note on documentation. While working, I decided to read the forword of pan docs, and lo and behold, its built for game developers, but there’s also a Complete GB Technical Reference which was supposedly built for emulator builders. I decided to check it out and see if it would be more useful than pan docs. BUT GUESS WHAT?? It had EVRYTHING EXCEPT AUDIO in there!! The word “audio” was used exactly once!! Apu wasn’t even mentioned!!! SO I have to continue with pan docs, which does have decentish detail for audio. Music I ended up listening to a lot of Chappell Roan, Cavetown, CITY LIMITS, and MAFUMAFU this session. Y’all should listen to the PEAK music of Sorry Ghost as well. See y’all next time!"}, {"post_id": 67901, "ts": "2026-09-14T06:27:32Z", "hours": 3.3375, "text": "Apu, like from The Simpsons ? Hello beautiful humans! We’re back, and I’ve finally stopped procrastinating on the APU! Let’s dive right in! CPU, GPU, TPU, NPU, LPU, DPU, and now APU?! I’ve been very scared of dealing with the APU, since it’s seemed like the hairiest thing to deal with, and it’s also the last part of the emulator core. The APU has four channels and mixing and fading and stuff I will get into. 2 Chan The first channel I started with was channel 2, since it is the simplest (1 has an extra feature, but otherwise, they’re the same) There are four things one needs to understand for these channels: Period Duty Length Envelope Duty is the waveform of the audio, like what percentage of the square wave is at level 1 vs level 0. This changes the timbre of the sound. Length is the amount of time the note plays for. It’s harder for devs to have to manually turn off the notes on their own b/c of timing. Envelope is the fade in/fade out of the sound Also, on channel 1, there is sweep, which changes envelope I think? I don’t know b/c Pan Docs is confusing for this (built for game developers it feels) Aaaanyways, that’s it from me for now, as I have a history in-class writing test thing tomorrow and I’ve already hit my 2½ hour goal for the day. Also, I was listening to Constant Companions and I realised I probably never listened to the album hard enough b/c “Clouddrop” is a lot more depressing than I remember, and I got ts on vinyl soo that’s something I will be embarrassed to tell my friends. Also, y’all should listen to Constant Companions to increase her listeners so that one day, Jamie can have her Paige again."}, {"post_id": 67127, "ts": "2026-09-13T05:58:53Z", "hours": 0.7461, "text": "Blink Twice Hello everyone! This is going to be a shorter devlog, so let’s dive right in! NOOOOOOOOOOOooooooooo When I set up the keypress handling a bit back, I wanted to use the default Retroarch keybinds so that anyone who had ever used Retroarch could use RGG without difficulty. The one discrepancy was that you only had to click escape one singular time to quit the emulator. I am no stranger to mistaken clicks, and today I realised this could be an issue for anyone who shares some of my clumsy tendencies. I figured it iwould be good to better copy Retroarch’s binds by making it so that you had to click Esc twice in quick succession to quit. A nice and easy change tha’s completion will probably force me to actually stop procrastinating audio/APU. Anyyywayyy, y’all should listen to CityLimits , DeltaFey , and Black Polish . I feel all of their music is great for programming. We’ll see if I can post another devlog today. ¡Adiós!"}, {"post_id": 66415, "ts": "2026-09-12T07:26:24Z", "hours": 0.7267, "text": "Frontman Hello everyone! This will probably, maybe, be the shortest amount of time for a devlog, so let’s hop to it! I’ll never let you go Last night, I implemented the backend funtionality and started on frontend functionality, a ’lil bit. Over the past 30 odd minutes, I finished up the saver struct. I wanted to have Saver be have a custom drop impl, but that would require it to carry an immutable reference to the CPU through the running of the emulator, which would break ownership rules due to having to make a mutable reference to the CPU to step it. What I did was I isolated the loop into a function and saved the return value to a variable, which gets propogated after the emulator attempts to save. The video is of me loading in a save into Pokémon Red! Comment which theme you want me to show off next!"}, {"post_id": 65817, "ts": "2026-09-11T06:36:19Z", "hours": 2.92, "text": "SAAAAAAAVVVEEE MEEEEEEE Hello everyone, we’re back with our daily devlog, this time before midnight! Let’s dive in! I’ll never forget you After finishing the MBCs, it was time to work on saving the game state. I had to save the data in binary form. Originally, I was going to use bincode, but I switched to postcard since bincode has been deprecated. I managed to get the plumbing working, so now I just have to implement the saving in main.rs. I had to keep this short since I need to go to bed. See y’all!"}, {"post_id": 65238, "ts": "2026-09-10T07:13:40Z", "hours": 1.3814, "text": "Rap It Up Hello friends! We’re back with the Daily Devlog! This one’s a quickie because the docs were (mostly) clear and I was listening to Rob Apollo while working. Let’s dive right in so I can run a mile on 5.5 hours of sleep rather than only 5! The Final Boss of MBCs When I started to do MBCs, the scope of this project was to do no MBC, MBC1, MBC2, MBC3, and maybe MBC5. I have been completing them largely in sequence: MBC1 MBC2 No MBC MBC3 (yesterday) This leaves only MBC5, today’s feature. I thought MBC5 was going to be difficult, but it was probably the easiest MBC besides no MBC, which isn’t even an MBC! MBC5 is basically a simpler and bigger version of MBC1, with two exceptions: Rumble: Apparently, cartridges can shake it down because some MBC5 variants have a rumble motor controlled by bit 3 of the CRAM selector. If there is rumble, that bit is dead. Chunks Every other MBC I’ve implemented followed this general rule: the ROM is exposed of 0x8000 bytes. Those bytes are split into four sections, each of 0x2000 bytes. Depending on which register you wrote to, it would be a different MBC command/"}, {"post_id": 64657, "ts": "2026-09-09T07:16:11Z", "hours": 2.3947, "text": "Third Time’s the Charm Hello everyone! I would have devlogged sooner, but I’ve spent most of the last time between this devlog and the last on a single feature, and the rest on YouTube. Let’s dive right in! Clock Iiiiit! After adding MBC2 and No MBC, the natural next step was MBC3. MBC3 is a lot like MBC1. It uses mostly the same register, many of which are direct copies or basically the same. There are only two key differences: MBC3 supports more RAM and has an RTC (real time clock). The more RAM wasn’t much to deal with, but the RTC kinda was. I messed up a good deal of the ticking on the irst pass of writing it, and I kept forgetting how if let worked, which did end up slowing me down. Anyway, it’s well past midnight and I need to smash my face in my pillow. My one regret is that I didn’t listen to enough Jamie Paige while devving."}, {"post_id": 64608, "ts": "2026-09-09T03:53:21Z", "hours": 3.1956, "text": "Clip It Hello comrades! We’re back with yet another devlog! Let’s dive in so I can get back to programming! Memory Control (Not 1984 Style) A bit ago, I mentioned that since cartridges were physical pieces of hardware, they could have logic chips of their own controlling their own RAM, so I would have to emulate those on top of the functionality of the actual GameBoy’s hardware. The next MBC (memory bank controller) to emulate was MBC2. MBC2 is weird. It has ROM banks as standard, but it also has 512 half-bytes of RAM built straight into the MBC chip for some reason. Those were a little weird to emulate, but altogether straightforward. I also had duplicated RAM indexing logic for both reading and writing to the cartridge RAM, which I marked todo in my code, so I had an excuse to do a quick refactor. After handling the weird little freak that is MBC2 was implementing No MBC. You may be asking, V, what’s there to emulate if there is nothing to emulate? And to that I say, it is once again weird and silly, just how we like it. Even if a game has no MBC, it can still have some RAM; it’s just always on and has no logic for accessing it. It ended up with me having to deal with a lil extra changes to handle some things I was not anticipating. Looks Like You’re Trying to Kill a Stick Figure A while ago, I watched a prime video (who was also the source of the idea for this project) about the Power of 10. I’ve since thought these rules are pretty neat and can serve to make code better (with possibly the exception of rule 7). I decided that I wanted to follow rule 10 for my code, so I foolishly set all lints to max pedantic, thinking I would have some lints, but I ENDED UP HAVING HUNDREDS OF MOSTLY USELESS LINTS, many of which ended up being me using as for casting causing less readable behaviour. Some of them were useful, such as me allocating MASSIVE arrays on the stack, even if they were boxed. Turns out Box::new([0; 0x20000]) creates the array on the stack first before memcopying to the heap in unoptimised builds, which is completely unnacceptable. Also, may of my other arrays were just straight up massive and unboxed, like the framebuffer or bus memory flat array. Anyways, after spending the bulk of this devlog’s time dealing with that, I gave up knowing the rest of the lints were harmless and pointless and clogging up important lints. In other news, despite listening to Jamie Paige for many hours this weekend, I still ended up needing my fren to tell me about it’s new song with Vane Lily and Yuma . I also showed them to Developer theme and it brought them some joy. I’m also very happy I got the thermometer question right, especially since I have an infrared thermometer on my desk and I had to find the correct emissivity setting to use on milk yesterday since my dad was making yoghurt. Anyway, I’ll end this before Grammarly shows me any more lints for this devlog."}, {"post_id": 63424, "ts": "2026-09-07T08:10:14Z", "hours": 5.5439, "text": "Can It Tetris? I say hello to you again, my near and dear viewers, as I begin this devlog with a frustratingly long amount of time logged. Let’s dive in so I can put the last few days behind me. DMing the Memory Last time I menioned that there was a feature I needed to add that allowed the cpu to write a byte to a register in memory to trigger a transfer of data from the address specified by the written byte to the OAM (object attribute memory — sprites and stuff; this transfer is the DMA OAM tranfer) because that was the main way stuff was written to oam b/c otherwise it would be too slow. Originally, I wrote a doubly recursive function kinda thing b/c it called read_byte() when it itself was called indirectly by read_byte() and it also called itself, but I scrapped it due to unguarenteed tail recursion and the slowness of repeatedly matching the section of memory being written to/read from, so I just used copy_within(). This also somehow fixed my input issues, so idk. Double Bug Whammy This is the part of the last few days that has been a slog and a horror. After adding the DMA OAM transfer, I tried running Tetris, and it worked…until I got into gameplay. The sprite showing the selected player amount on the start screen was working and it would respond to user input. However, when it hit actual gameplay, it wouldn’t render the tetromino sprites until they were baked into the background as part of setting in place. To try and diagnose the problem, I ran blarrg’s big cpu_instrs.gb test ROM, and it stopped rendering frames in the middle of the 3rd test, the standalone ROM for which had been working without a flaw. Testing in mGBA through RetroArch (I don’t have mGBA installed standalone on this device), I found that the test takes a second to run, but doesn’t cause the whole emulator to hang. Also, since new frames weren’t being completed, it wouldn’t poll for input since it only polls on a frame completion, which meant I had to close the terminal session to quit the emulator. I spent hours running tests just to find that it was getting hit by a STOP instruction it couldn’t escape. I checked Pan Docs and found there had been some unimplemented STOP behaviours. After implementing those and it still not working, I set STOP to only stop for a single cycle, which let the test ROM pass all the tests with no timing issues. After that, within 2 minutes, I was able to find an incorrect == (should have been !=) in my sprite attributes bit 7 sprite priority control, allowing Tetris to finally work!!! Next up is: MBC2 MBC3 MBC5 Saving Audio TUI rather than bare CLI a lib.rs so others can write a frontend on top of Rusty Game Girl A style guide and making my code compliant (hard to do while writing and over a codebase who’s size is at least 4.5x bigger than anything else I’ve ever worked with) Unit tests also here’s a video of me selling at Tetris b/c I wasn’t listening to Jamie Paige while playing"}, {"post_id": 61315, "ts": "2026-09-04T05:11:57Z", "hours": 2.0253, "text": "RAM or ROM Hello everyone! I’ve finally logged a normal amount of time again! Let’s dive right in! MBC Say One! I managed to bang out my first supported MBC (memory bank controller), a lot of it done while walking on the treadmill this morning. I had already implemented the ROM bank switching by this morning, so next I had to implement switching RAM banks, which has some pretty funky indexing. Grace Hopper Naturally, what does one do when they have a hopefully working MBC system, you try a rom!. I only found out tetris doesn’t use an mbc after removing my basic mbc substitute stuff that enabled me to run simple roms. Speaking of Tetris, the screenshots are me attempting to run tetris iwth different themes (the themes work, tetris doesn’t which we’ll get to). The themes in order Wisteria GBGRreen Jungle Developer (Don’t worry, there’s more themes, and you can always submit an issue or PR with a new theme) Let’s get back to BUGSSSS!! When I tried to run tetris, it wasn’t accepting input and the arrow showing which options are selected was unpresent. These issues are the next things to resolve. I found that there was a critical error in the read methods on my button data stuct Here is the commit with the changes in case you are curious. I found out that there is a spot in memory you can write to to load a sprite from some address into OAM. This is the next thing I will be implementing before I try finding the next issue with input so I can at least see some pretty sprites. Anyway, see y’all tomorrow after I hopefully get tetris working!"}, {"post_id": 60661, "ts": "2026-09-03T05:07:00Z", "hours": 3.2858, "text": "You can switch the banks, switch banks, swi- swi- swi- swi- swi- — until you’re full of life Hello everyone! I changed the name of my project directory, so I wasn’t seeing my hackatime or had the ability to write a devlog until I realised the problem, so let’s just dive right in! Bank It The next thing to deal with was bank switching and better behaviour for handling roms. Cartridges are physical objects, and thus can have extra RAM and storage for the gameboy. These are controlled by MBCs (memory bank controllers). The most common ones are MBC1, MBC2, MBC3, and MBC5. For this project, for now, I am only emulating 1, 2, and 3. In the future, I will probably add more controllers once the full architecture is done for at least these three so I have an idea of hwo the skeletons should look. The first step of bank switching was having the rom portion of the memory be read-only and writes to it do certain mbc actions, like enable ram or switch banks or switch ram. I am currently implementing the switching of banks. I need to get back to MBC1-specific flags and writes. See y’all next time!"}, {"post_id": 59742, "ts": "2026-09-01T17:50:39Z", "hours": 7.8103, "text": "Human Error Hello everyone! I got too much in the flow and in the RAGE, which is why I logged nearly 8 hours since the last devlog! Let’s get in the weeds! Loading Bay I added ROM loading!!!!! I can actually load a test ROM in now!!! Naturally, I immediately loaded one in, and that brings us to our first issue. Speed? Issues When I was loading in a test ROM, the text of the name of the test would peek from the bottom of the screen, then jump up and not show passed. I probably spent 2 HOURS on trying to fix this. I thought it was a speed issue, and I ended up making a ton of optimisations and added alternate screen switching. BUT GUESS WHAT??? I zoomed out my terminal window, and there it was: Passed. I would share a screenshot, but I am currently working on changes to load_rom() that break the emulator currently (bank switching). Don’t Stop I mentioned a while ago that I had to emulate a bug in the hardware that caused the next instruction to occur twice if HALT is called when there is an interrupt pending and IME (interrupt master enable) is off. My implementation caused it to infinitely stall instead. So I fixed that bit, but the HALT bug test rom still wasn’t running the tests, so I found that LY couldn’t be read from memory, so fixing that fixed the thing, allowing the tests to run and FAIL. So I had to run a disassembler on the HALT bug test to find the exact mechanics of the HALT bug, then write it. Even though the values for the result were correct, it still wouldn’t pass. This was because of an oversight on my part when I was writing the interrupt handling. The IF byte (0xff0f, pending interrupts, whether or not they’re enabled) in hardware always has its highest 3 bits as 1, and I didn’t think it was necesary to implement this when I was writing interrupt handling, so I ignored it. And finally, fixing that, the HALT bug worked. The screenshots are: Gambatte failing the test. mGBA passing. RustBoy (May change to GameGirl/RustGirl for uniqueness and searchability) passing. See y’all next time!"}, {"post_id": 58276, "ts": "2026-08-30T17:25:28Z", "hours": 4.9708, "text": "You’ve Been Selected for a “Random” Screening Hello Everyone! We are back! I don’t know how it’s already been over 4½ hours! Let’s dive straight into the progress! They Know How to Push Each Other’s Buttons After making constructors for the structs and refactoring the button data struct, I added a loop to handle all waiting button presses and set the data in the button data struct. Not particularly complicated, though it was fun to test something that actually did something. Staaaaahhp Instructions take a set amount of time, so it is necessary to have a pause, since even a TI-84 (buy the CG-50 or HP Prime G2 instead; don’t get the CG-100, it lacks plugin support, which is why I recommend the CG-50 at all) is faster. Since systems can’t pause for a single tick, it pauses frame-by-frame instead. How to Exit Neovim I didn’t take the time to add a break condition, so I added a check to see if the escape key had been pressed, and if it had, it quit the application. Show Me the Money The biggest development was the render loop. Now, there is actually a visual to see and play with. Paint a Pretty Picture I also felt it would be boring if there was only one colour theme, so I added an enum along with PixelValue => Colour matching so that you can pick a theme! Speaking of picking… Staring at a Config File Since the user has to be able to pick a theme, I felt it would be good to have config.toml support for ease of use."}, {"post_id": 57137, "ts": "2026-08-29T07:35:57Z", "hours": 5.585, "text": "Building Buttons Hi everyone!! It seems that time has slipped away from me yet again, so let’s dive straight into your scheduled content! Pushy Pushy Buttons After dealing with sprites (oh! the horror! the suffering! the despair!), it was time to deal with a new kind of beast: the joypad. Implementing joypad functionality meant it was time to make a frontend (ew) instead of just building subsystems and cobbling them together later. This meant it was time to learn stuff about the crossterm crate and make some constructor functions. The screenshot is my current struct for storing the button push data. It used to be 8 bools, but that was 7 bytes too many, so it’s just a u8 wrapper now. Cross-multiply Those Terms! Crossterm is a crate for dealing with terminal keyboard interrupt and such. The first and only thing I’ve done so far was make a wrapper with a custom destructor for Keyboard enhancement flags. struct TerminalGuard { enhancement_pushed : bool , } impl TerminalGuard { fn new () -> std :: io :: Result < Self > { enable_raw_mode () ? ; let enhancement_pushed = supports_keyboard_enhancement () .unwrap_or ( false ); if enhancement_pushed { execute! ( stdout (), PushKeyboardEnhancementFlags ( KeyboardEnhancementFlags :: REPORT_EVENT_TYPES ) ) ? ; } Ok ( Self { enhancement_pushed }) } } impl Drop for TerminalGuard { fn drop ( & mut self ) { if self .enhancement_pushed { let _ = execute! ( stdout (), PopKeyboardEnhancementFlags ); } let _ = disable_raw_mode (); } } Computah, Make me a New Computah Since it was time to write a main() fn, it meant I also had to write constructor functions for CPU and all it’s subsidiaries, since I’ve not bothered to do it before. That’s it folks! See y’all next time!"}, {"post_id": 53353, "ts": "2026-08-23T21:11:57Z", "hours": 2.6878, "text": "Crack Open a Sprite Hello everyone! This dvelopment took longer than expected, so let’s just dive right in! County Rogues, Bring me lOAM After I finished the window layer, it was time to deal with sprites. Where each sprite is on the screen, which tiles to use, and sprite-specific render attributes are stored in a section of memory called OAM. 40 sprites’ data can be stored in OAM, each 4 bytes: the x position, y position, which tile(s) to use, and the attributes of that sprite (height, when to render, etc.) Unlike the background and window, sprites have a lot more moving parts. Here’s my code for rendering the sprites to the buffer. fn render_sprites ( & mut self ) { let lcdc = self .read_byte ( 0xff40 ); if lcdc >> 1 & 0x1 == 0 { return ; } let mut indexes = ( 0 .. 40 ) .collect :: < Vec < u16 >> (); indexes .sort_unstable_by_key (| n | ( self .read_byte ( 0xfe01 + n * 4 ) as i16 - 8 , * n )); let indexes : Vec <& u16 > = indexes .iter () .rev () .collect (); let height = if lcdc >> 2 & 0x1 == 0 { 8 } else { 16 }; for n in indexes { let entry = 0xfe00 + n * 4 ; let sprite_y = self .read_byte ( entry ) as i16 - 16 ; let sprite_x = self .read_byte ( entry + 0x1 ) as i16 - 8 ; let tile_index = self .read_byte ( entry + 0x2 ); let attributes = self .read_byte ( entry + 0x3 ); let palette = if ( attributes >> 4 ) & 0x1 == 0 { 0xff48 } else { 0xff49 }; for row in sprite_y .clamp ( 0 , 144 ) as u8 .. ( sprite_y + height ) .clamp ( 0 , 144 ) as u8 { for column in sprite_x .clamp ( 0 , 160 ) as u8 .. ( sprite_x + 8 ) .clamp ( 0 , 160 ) as u8 { if ( attributes >> 7 ) & 0x1 == 1 && self .bit_7_check ( row , column ) { continue ; } let row_in_sprite = if ( attributes >> 6 ) & 0x1 == 0 { ( row as i16 - sprite_y ) as usize } else { height as usize - ( row as i16 - sprite_y ) as usize - 1 }; let column_in_sprite = if ( attributes >> 5 ) & 0x1 == 0 { ( column as i16 - sprite_x ) as usize } else { 7 - ( column as i16 - sprite_x ) as usize }; let which_tile = if row_in_sprite < 8 { ( 0 , 0 ) } else { ( 1 , 8 ) }; let pixel = self .index_to_tile_unsigned ( tile_index + which_tile .0 ) [ row_in_sprite - which_tile .1 ][ column_in_sprite ]; if pixel == TilePixelValue :: Zero { continue ; } let value = self .palette_lookup ( pixel , palette ); self .gpu .set_pixel ( row , column , value ); } } } } fn bit_7_check ( & self , row : u8 , column : u8 ) -> bool { if self .window_active_for_pixel ( column , row ) { if self .window_pixel_value ( column , row ) == TilePixelValue :: Zero { return false ; } return true ; } if self .background_pixel_value ( column , row ) == TilePixelValue :: Zero { return false ; } true } This also excludes any boilerplate for window and background that I had to reuse. The most annoying part was dealing with ordering indexes efficiently, because the x value of the sprite is used as the method for sprite precedence over layers. I didn’t realise that sort_unstable_by_key dealt with like tuples and you could use that for precedence and tiebreaking. Anyway, that’s all folks! see y’all next time!"}, {"post_id": 51464, "ts": "2026-08-21T05:58:56Z", "hours": 1.9039, "text": "Ogres are like Onions Hello everyone! This time, I’ve fortunately logged a normal amount of time. Let’s dive in! Close the Window! After the background rendering was complete, it was time to work on the next layer: the window. The window is exactly as it sounds — the layer between sprites and the background that can have ui and stuff. The window starts at a position dictated by wx and wy (0xff4b and 0xff4a in memory), and those two dictate the top-left corner of the window layer (though wx - 7 is the actual top-left x coord). If a game wants to have a window layer that ends, it will switch off the window layer BETWEEN scanlines, which my current implementation cannot do because I render the whole frame at VBlank (after all scanlines) for simplicity, and it’s not strictly necessary. The code implementation of the window layer was relatively similar to background: fn window_active_for_pixel ( & self , screen_x : u8 , screen_y : u8 ) -> bool { if ( self .read_byte ( 0xff40 ) >> 5 ) & 0x1 == 0 { return false ; } let wy = self .read_byte ( 0xff4a ); let wx = self .read_byte ( 0xff4b ); if screen_y >= wy && screen_x >= wx .saturating_sub ( 7 ) { return true ; } false } fn window_pixel_value ( & self , screen_x : u8 , screen_y : u8 ) -> TilePixelValue { let window_y = screen_y - self .read_byte ( 0xff4a ); let window_x = screen_x + 7 - self .read_byte ( 0xff4b ); let window_tile_map_index = ( window_y / 8 ) as u16 * 32 + ( window_x / 8 ) as u16 ; let window_tile_map_base = if ( self .read_byte ( 0xff40 ) >> 6 ) & 0x1 == 0 { 0x9800 } else { 0x9c00 }; let tile = & self .index_to_tile ( self .read_byte ( window_tile_map_base + window_tile_map_index )); tile [( window_y % 8 ) as usize ][( window_x % 8 ) as usize ] } The next step is the final layer: Sprites! Sprites will be more involved and unique compared to background and window. I still can’t believe the Sharp SM83 did all this for developers. See y’all next time!"}, {"post_id": 50574, "ts": "2026-08-19T19:34:48Z", "hours": 4.8942, "text": "No More GPU Crisis Hello everyone! It seems that, yet again, time has slipped away from me! Let’s dive back in! STATistics When the chip swaps rendering modes, depending on whether the STAT register enables that specific interrupt, it will trigger a STAT interrupt (priority level 2). I had to set up a loop to shift modes so that each mode can easily be fully handled (see screenshot). This was just a matter of finding out how long each mode and line lasted, relatively simple. Render Engines After this, I had to start adding rendering features. There are three components to the rendering: Background Window Sprites The first, and easiest part, was the background. Which tiles compose the background is dictated by a series of bytes after either 0x9800 or 0x9c00, which have references to tiles in memory. Pretty simple. fn render_to_buffer ( & mut self ) { for row in 0 .. 144 { for column in 0 .. 160 { self .gpu .set_pixel ( row , column , self .bgp_palette_lookup ( self .pixel_value ( column , row )), ); } } } fn pixel_value ( & self , screen_x : u8 , screen_y : u8 ) -> TilePixelValue { let map_y = self .read_byte ( 0xff42 ) .wrapping_add ( screen_y ); let map_x = self .read_byte ( 0xff43 ) .wrapping_add ( screen_x ); let tile_map_index = ( map_y / 8 ) as u16 * 32 + ( map_x / 8 ) as u16 ; let tile_map_base = if ( self .read_byte ( 0xff40 ) >> 3 ) & 0x1 != 0 { 0x9c00 } else { 0x9800 }; let tile : & Tile = & self .index_to_tile ( self .read_byte ( tile_map_base + tile_map_index )); tile [( map_y % 8 ) as usize ][( map_x % 8 ) as usize ] } fn index_to_tile ( & self , index : u8 ) -> Tile { let unsigned = ( self .read_byte ( 0xff40 ) >> 4 ) & 0x1 != 0 ; if unsigned { return self .gpu.tile_set [ index as usize ]; } let signed_index = ( 256 + ( index as i8 as i16 )) as usize ; self .gpu.tile_set [ signed_index ] } fn bgp_palette_lookup ( & self , value : TilePixelValue ) -> PixelValue { let index = value as u8 ; match ( self .read_byte ( 0xff47 ) >> ( index * 2 )) & 0b11 { 0 => PixelValue :: White , 1 => PixelValue :: LightGray , 2 => PixelValue :: DarkGray , 3 => PixelValue :: Black , _ => panic! ( \"Unknown shade\" ), } } The next step is the window, then the sprites. Sprites will be hard due to their varied size. See y’all next time!"}, {"post_id": 48739, "ts": "2026-08-17T08:32:42Z", "hours": 2.0075, "text": "Div(isions of time) Hello everyone! This time, I actually remembered to make a devlog! Let’s dive in! Div(vy up Labour) Remember how I added the number of ticks each instruction took as a return value from the execute() fn? Well, it actually got put to use now! There is an internal 16-bit timer in the Sharp SM83, the upper 8 bits of which are exposed at memory address 0xff04. Programming this was pretty easy once I realised I could have read_byte() and write_byte() directly access the counter (Originally, I was going to have an update_div thingy probably and have the bte in memory and the accumulator be separate rather than this simpler approach). pub fn read_byte ( & self , address : u16 ) -> u8 { let address = address as usize ; match address { VRAM_BEGIN ..= VRAM_END => self .gpu .read_vram ( address - VRAM_BEGIN ), 0xff04 => ( self .div_accumulator >> 8 ) as u8 , _ => self .memory [ address ], } } pub fn write_byte ( & mut self , address : u16 , byte : u8 ) { let address = address as usize ; match address { VRAM_BEGIN ..= VRAM_END => self .gpu .write_vram ( address - VRAM_BEGIN , byte ), // Any write to div resets it and its accumulator 0xff04 => self .div_accumulator = 0 , _ => self .memory [ address ] = byte , }; } Now, last time, I set up interrupts, so with div set up, it was time to set up timer interrupts. TIMA Time What actually triggers timer interrupts, rather than div, is a separate timer called TIMA (0xff05 in memory). TIMA has settings set in TAC (0xff07 in memory). Bits 0 and 1 of TAC dictate which bit of the DIV accumulator (let’s call it divacc) to watch to see when TIMA should be incremented. Bit 2 dictates whether TIMA should be incremented at all. TIMA gets incremented whenever the indicated bit of divacc falls from 1 → 0. This can sometimes be as few as every 16 ticks, and since instructions can take up to 24 ticks, this can cause the bit to go from 1 → 0 → 1, which means that you can’t just check the new divacc against the old one to see whether the bit fell, though I tried this as a temporary solution. This is what I had to do instead (see screenshot). I wish it could be prettier, but what can you do? Anyways, y’all, see you next time!"}, {"post_id": 47323, "ts": "2026-08-15T06:28:37Z", "hours": 2.7594, "text": "Interrupting Cow Hello everyone! This took a lot longer than I expected due to a large amount of me getting confused by how interrupts work. Let’s dive in! Halt! After dealing with tick returns, the next step was dealing with interrupts. There are two bytes in memory that concern interrupts. One is IE (0xffff), which tells which interrupts to listen for, and the other is IF (0xff0f), which tells which interrupts are currently pending. Dealing with these was easy enough, but there was another thing that needed addressing: The Halt Bug The halt bug is a known bug that, should Halt be ran when there are interrupts pending and the IME (interrupt master enable I think) is off, it doesn’t halt and the next instruction runs twice. I was both confused by how this behaved and how it was triggered, which caused issues with my code that had to be addressed. I wasn’t able to deal with anything besides interrupt handling , so that’s all for now!"}, {"post_id": 46748, "ts": "2026-08-14T07:06:18Z", "hours": 3.7389, "text": "Ticking Time Bomb Hello everyone! It seems that, yet again, I have forgotten to make a devlog for a few hours, so let’s dive in! Fortunately, not much has happened. 16 GB of VRAM As I said in my last devlog, it was time to get started on graphical stuff. I set up a GPU struct with a separate VRAM, and set up the memory to write to that VRAM instead of the regular bus when it’s supposed to. This was also the last part of the docs I’ve been following, so I’ve been on my own since this point. It’s a little pesky that my best resources on how the GB works are now Claude Code and a random opcode table, but what can you do? Tick-Tock Goes the Clock Different operations in the GB’s processor take different numbers of clock cycles to do, so I had to add this as a second return value to execute to add slowing in the future. This was the majority of the hours I logged, staring at the opcode table and trying to figure out the patterns in the timings. Anyway, see y’all next time! These are some silly things that will probably land for at least some of you."}, {"post_id": 46058, "ts": "2026-08-13T00:13:53Z", "hours": 1.3156, "text": "Where are my keys? Hello everyone! We are back with a little news, so let’s dive right in! Hashmap, was not, in fact, the solution The mapping from opcode to instruction enum (fn from_byte()) is finally done. Not much to say except that it was a very tedious process. While working, I realised I hadn’t implemented ADD SP, i8 (addassign a signed integer to the stack pointer) yet, so I quickly added that. Unfortunately, that’s all folks! Next, I’m going to start dealing with graphics."}, {"post_id": 45921, "ts": "2026-08-12T18:07:20Z", "hours": 1.9139, "text": "Miscellany Hello everyone! Let’s keep this quick because I have much tedium to get through. What the Hecc is JR?! I finished most of instructions, so I needed to figure out which ones I forgot. I was looking at the opcode table and I realised I had no idea what JR or DAA did, so I had to figure out what they did. JR is just a relative jump rather than an absolute one, and DAA does some number formatting after an arithmetic op for better display. Let’s Table this Discussion Since the instructions that I can implement right now (very nearly all of them, except for stop and interrupts, etc.), it’s been time to update the Instruction::from_byte() fn with all the new instructions. I’m still doing this, and it is purely looking at the opcode table, entering something, looking at the opcode table again, looking at what I wrote, and repeating this over and over and over again. I’m not even done with this! Anyway, enjoy a hashmap meme that doesn’t apply to me b/c I am using a match"}, {"post_id": 45633, "ts": "2026-08-12T08:00:46Z", "hours": 1.6425, "text": "Pushpops Hello everyone! This time, I actually logged a normal amount of time! Let’s dive in! Loading Shipments I finally finished up the LD instruction! once I got through the weird targets, everything was smooth sailing. Stack Overflow Once LD was done, it was time to implement the stack. If you don’t know what (a/the) stack is, reference the wikipedia screenshot. For the GB, the two methods are PUSH and POP, and the stack actually goes down in memory from the end of the memory bus rather than from the beginning. Dear Functional Bros When I finished implementing the stack, which I though was going to be a lot harder, it was time to add functions to it. This was pretty simple: a CALL instr. that jumps to a point and puts the place where it left off on the stack and a RET instr. that goes to the last location on the stack."}, {"post_id": 45566, "ts": "2026-08-12T04:30:55Z", "hours": 3.2408, "text": "Loading Jump Time Hello everyone! I lost track of time, and it appears I have once more logged too many hours with no devlog, yet no excuses I have. Let’s get right to it! HL and D8 A lot of Instructions that I had already made have an HL target, in which they use the value in the joint register HL as an address in memory, and perform the operation on that byte. Up until this point, all HL targets were todos, but, since I had created the memory bus, I was able to quickly wire up those targets. A lot of those same instructions could also take the next byte in memory as the input (D8) so I wired those up too (though before HL). I actually wasn’t sure whether HL targets used a different memory bus than the one used for operations. JP Morgan The next instruction to deal with was JP (jump). Based on different conditions (the state of the zero flag or carry flag, or always) it would or wouldn’t jump to the address encoded over the next two bytes. Probably one of the easier instructions I’ve made. Loading… After JP was load. LD just copies data from one place to another. There are so many variations that I can’t talk about all of them, and there my code is too long for this devlog, so: Byte: Load a byte from point a to point b Word: just like the Byte type except with 16-bit valuesAFromIndirect: load the A register with the contents from a value from a memory location whose address is stored in some locationIndirectFromA: load a memory location whose address is stored in some location with the contents of the A registerAFromByteAddress: Just like AFromIndirect except the memory address is some address in the very last byte of memory.ByteAddressFromA: Just like IndirectFromA except the memory address is some address in the very last byte of memory. This is the opcode table I’ve been using"}, {"post_id": 45409, "ts": "2026-08-11T20:46:14Z", "hours": 4.115, "text": "Timeliness Rather unfortunately, a lot of work has been done since the last devlog due to yesterday’s hackatime outage. Let’s get started! House of Memories As I mentioned in the last devlog, the next step was to create a program counter (pc, marks where in the program we are) and the memory bus (with all the instructions we have been emulating for the GB to run, and others) Bite the Code The instructions are not stored as plain text of enums, they are stored as bytes , one byte for each combination of instruction and target. I thus have to map each bytecode to each instruction that I have added so far. Not English Class! Anything but English! There are much more than 256 different bytecodes; there are actually nearly 512. To accomplish this and still have memory be 1 byte, there is a bytecode prefix , 0xcb, which tells the chip that the real instruction is the next one, and to look it up on a different table. PCMR To go through each instruction, I added a step function: fn step ( & mut self ) { let ( instruction_byte , prefixed ) = { let temp_byte = self .bus .read_byte ( self .pc ); if temp_byte == 0xcb { ( self .bus .read_byte ( self .pc + 1 ), true ) } else { ( temp_byte , false ) } }; let next_pc = if let Some ( instruction ) = Instruction :: from_byte ( instruction_byte , prefixed ) { self .execute ( instruction ) } else { panic! ( \"Unknown instruction found for: 0x{:x}\" , instruction_byte ); }; self .pc = next_pc ; } As you can see, execute returns the next pc value, something it didn’t do before. So, I had to go and make all the match arms blocks that do their associated action and also return self.pc.wrapping_add(1) for non-0xcb functions and self.pc.wrapping_add(2) for 0xcb functions, since no function that modifies the pc has been added yet. Next is added yet more instructions! See y’all next time!"}, {"post_id": 44287, "ts": "2026-08-09T22:52:31Z", "hours": 2.3447, "text": "Registering New Data Hello Everyone! You know what they say: mo’ code mo’ devlogs. To the right, to the right, to the right, the right, the right I finished up all the instructions that act on register data!!! Specifically, these ones: ADD - Add the value of the target register to the A register. ADDHL (add to HL) - just like ADD except that the target is added to the HL register ADC (add with carry) - just like ADD except that the value of the carry flag is also added to the number SUB (subtract) - subtract the value stored in a specific register with the value in the A register SBC (subtract with carry) - just like ADD except that the value of the carry flag is also subtracted from the number AND (logical and) - do a bitwise and on the value in a specific register and the value in the A register OR (logical or) - do a bitwise or on the value in a specific register and the value in the A register XOR (logical xor) - do a bitwise xor on the value in a specific register and the value in the A register CP (compare) - just like SUB except the result of the subtraction is not stored back into A INC (increment) - increment the value in a specific register by 1 DEC (decrement) - decrement the value in a specific register by 1 CCF (complement carry flag) - toggle the value of the carry flag SCF (set carry flag) - set the carry flag to true RRA (rotate right A register) - bit rotate A register right through the carry flag RLA (rotate left A register) - bit rotate A register left through the carry flag RRCA (rotate right A register) - bit rotate A register right (not through the carry flag) RLCA (rotate left A register) - bit rotate A register left (not through the carry flag) CPL (complement) - toggle every bit of the A registerBIT (bit test) - test to see if a specific bit of a specific register is set RES (bit reset) - set a specific bit of a specific register to 0 SET (bit set) - set a specific bit of a specific register to 1 SRL (shift right logical) - bit shift a specific register right by 1 RR (rotate right) - bit rotate a specific register right by 1 through the carry flag RL (rotate left) - bit rotate a specific register left by 1 through the carry flag RRC (rorate right) - bit rotate a specific register right by 1 (not through the carry flag) RLC (rorate left) - bit rotate a specific register left by 1 (not through the carry flag) SRA (shift right arithmetic) - arithmetic shift a specific register right by 1 SLA (shift left arithmetic) - arithmetic shift a specific register left by 1 SWAP (swap nibbles) - switch upper and lower nibble of a specific register Some of the bitwise ops for rotation were a little difficult, but I was able to troubleshoot. Next is the game ROM, the program counter, prefix instructions, and jump instructions!"}, {"post_id": 43849, "ts": "2026-08-09T08:49:27Z", "hours": 1.5925, "text": "Untargeted Improvements Hello everyone! New day, new devlog! I added some new instructions! CCF — Clear Carry Flag SCF — Set Carry Flag RRA — Rotate Right A RLA — Rotate Left A RRCA — Rotate Right Circular A RLCA — Rotate Left Circular A CPL — Complement Bits of A Register Figuring out some of the bitwise logic for the rotation command, especially RLA (and because I was working in the car) was kind of difficult. fn rra ( & mut self ) -> u8 { let carry = if self .registers.f.carry { 1 } else { 0 }; let value = self .registers.a ; self .registers.f.zero = false ; self .registers.f.subtract = false ; self .registers.f.carry = value & 0x1 == 1 ; self .registers.f.half_carry = false ; ( value >> 1 ) | ( carry << 7 ) } fn rla ( & mut self ) -> u8 { let carry = if self .registers.f.carry { 1 } else { 0 }; let value = self .registers.a ; self .registers.f.zero = false ; self .registers.f.subtract = false ; self .registers.f.carry = ( value >> 7 ) & 0x1 == 1 ; // Originally I was doing the bit isolation before the bitshift, // and I forgot about the bitshift initially, // so I was just doing a bit isolation and then clippy complained b/c it would never be true. self .registers.f.half_carry = false ; ( value << 1 ) | carry } Next are BIT, RES, and SET, and then more instructions! The image is a question I saw on Protobowl a few months back."}, {"post_id": 43618, "ts": "2026-08-09T00:03:21Z", "hours": 2.3414, "text": "Target Practise Hello everyone! We’re back with another devlog! Instructor I managed to finish up all the instructions (minus the HL sections of those because that uses the value in the combined HL register as a pointer to memory, which I did not yet add) that were already in the enum (screenshot from last devlog). I also just added the INC and DEC instructions, which increment or decrement the value in the target register or byte in memory pointed to by HL. It was a bit of a hassle to figure out how I should do the target enums, since there is an 8 and 16 bit version of each INC and DEC, each with different targets. INC8 can target the byte of memory pointed to by HL, whereas INC16 can point to HL itself and the other 3 16 bit registers. See y’all next time!"}, {"post_id": 43150, "ts": "2026-08-08T08:10:23Z", "hours": 1.7228, "text": "We’ve Brought Some New Additions After finishing up the LZU and its associated rating, it’s time to get back to work on this project! There are no Antarctic Pyramids Ever since I heard about the wonderful site that is Codeberg, I’ve wanted to swap to there, but the thing stopping me was this challenge, because I thought it only worked with GitHub, but when I was rating projects, I found that one of them was on Codeberg. Because of that, I swapped over this repo to Codeberg, which it made very easy, and continued on my coding journey. Yesop Like I said in my last devlog, I have to implement the instructions for the CPU in code! I managed to create most of the implementations for AddHL and Adc, only stopped by the fact that I still need to emulate memory. The next step is to finish Sub and the rest of the ops in this enum, then the rest of the instructions! See y’all next time!"}, {"post_id": 42269, "ts": "2026-08-06T21:57:24Z", "hours": 1.3686, "text": "Register Additions Welcome to my first devlog! I am building a GameBoy emulator in Rust!! Didn’t Register The first step was emulating the registers. The GB has 8 8-bit registers, and sometimes they are combined 2-together to form 16-bit registers. 1 of the registers, register f, is the flag register. It’s upper four bits are different flags for things like carry and half carry. Didn’t Compute Next, I had to start making the cpu struct. It has to be capable of executing commands, like ADD, so there is a command enum with different commands, though it only has ADD right now, and I still need to figure out all the possible ADD targets."}]...
|
Title: Riggurly by @infinite_silly | Stardance
De Title: Riggurly by @infinite_silly | Stardance
Devlogs:
Register Additions Welcome to my first devlog! I am building a GameBoy emulator in Rust!! Didn’t Register The first step was emulating the registers. The GB has 8 8-bit registers, and sometimes they are combined 2-together to form 16-bit registers. 1 of the registers, register f, is the flag register. It’s upper four bits are different flags for things like carry and half carry. Didn’t Compute Next, I had to start making the cpu struct. It has to be capable of executing commands, like ADD, so there is a command enum with different commands, though it only has ADD right now, and I still need to figure out all the possible ADD targets.
We’ve Brought Some New Additions After finishing up the LZU and its associated rating, it’s time to get back to work on this project! There are no Antarctic Pyramids Ever since I heard about the wonderful site that is Codeberg, I’ve wanted to swap to there, but the thing stopping me was this challenge, because I thought it only worked with GitHub, but when I was rating projects, I found that one of them was on Codeberg. Because of that, I swapped over this repo to Codeberg, which it made very easy, and continued on my coding journey. Yesop Like I said in my last devlog, I have to implement the instructions for the CPU in code! I managed to create most of the implementations for AddHL and Adc, only stopped by the fact that I still need to emulate memory. The next step is to finish Sub and the rest of the ops in this enum, then the rest of the instructions! See y’all next time!
Target Practise Hello everyone! We’re back with another devlog! Instructor I managed to finish up all the instructions (minus the HL sections of those because that uses the value in the combined HL register as a pointer to memory, which I did not yet add) that were already in the enum (screenshot from last devlog). I also just added the INC and DEC instructions, which increment or decrement the value in the target register or byte in memory pointed to by HL. It was a bit of a hassle to figure out how I should do the target enums, since there is an 8 and 16 bit version of each INC and DEC, each with different targets. INC8 can target the byte of memory pointed to by HL, whereas INC16 can point to HL itself and the other 3 16 bit registers. See y’all next time!
Untargeted Improvements Hello everyone! New day, new devlog! I added some new instructions! CCF — Clear Carry Flag SCF — Set Carry Flag RRA — Rotate Right A RLA — Rotate Left A RRCA — Rotate Right Circular A RLCA — Rotate Left Circular A CPL — Complement Bits of A Register Figuring out some of the bitwise logic for the rotation command, especially RLA (and because I was working in the car) was kind of difficult. fn rra ( & mut self ) -> u8 { let carry = if self .registers.f.carry { 1 } else { 0 }; let value = self .registers.a ; self .registers.f.zero = false ; self .registers.f.subtract = false ; self .registers.f.carry = value & 0x1 == 1 ; self .registers.f.half_carry = false ; ( value >> 1 ) | ( carry << 7 ) } fn rla ( & mut self ) -> u8 { let carry = if self .registers.f.carry { 1 } else { 0 }; let value = self .registers.a ; self .registers.f.zero = false ; self .registers.f.subtract = false ; self .registers.f.carry = ( value >> 7 ) & 0x1 == 1 ; // Originally I was doing the bit isolation before the bitshift, // and I forgot about the bitshift initially, // so I was just doing a bit isolation and then clippy complained b/c it would never be true. self .registers.f.half_carry = false ; ( value << 1 ) | carry } Next are BIT, RES, and SET, and then more instructions! The image is a question I saw on Protobowl a few months back.
Registering New Data Hello Everyone! You know what they say: mo’ code mo’ devlogs. To the right, to the right, to the right, the right, the right I finished up all the instructions that act on register data!!! Specifically, these ones: ADD - Add the value of the target register to the A register. ADDHL (add to HL) - just like ADD except that the target is added to the HL register ADC (add with carry) - just like ADD except that the value of the carry flag is also added to the number SUB (subtract) - subtract the value stored in a specific register with the value in the A register SBC (subtract with carry) - just like ADD except that the value of the carry flag is also subtracted from the number AND (logical and) - do a bitwise and on the value in a specific register and the value in the A register OR (logical or) - do a bitwise or on the value in a specific register and the value in the A register XOR (logical xor) - do a bitwise xor on the value in a specific register and the value in the A register CP (compare) - just like SUB except the result of the subtraction is not stored back into A INC (increment) - increment the value in a specific register by 1 DEC (decrement) - decrement the value in a specific register by 1 CCF (complement carry flag) - toggle the value of the carry flag SCF (set carry flag) - set the carry flag to true RRA (rotate right A register) - bit rotate A register right through the carry flag RLA (rotate left A register) - bit rotate A register left through the carry flag RRCA (rotate right A register) - bit rotate A register right (not through the carry flag) RLCA (rotate left A register) - bit rotate A register left (not through the carry flag) CPL (complement) - toggle every bit of the A registerBIT (bit test) - test to see if a specific bit of a specific register is set RES (bit reset) - set a specific bit of a specific register to 0 SET (bit set) - set a specific bit of a specific register to 1 SRL (shift right logical) - bit shift a specific register right by 1 RR (rotate right) - bit rotate a specific register right by 1 through the carry flag RL (rotate left) - bit rotate a specific register left by 1 through the carry flag RRC (rorate right) - bit rotate a specific register right by 1 (not through the carry flag) RLC (rorate left) - bit rotate a specific register left by 1 (not through the carry flag) SRA (shift right arithmetic) - arithmetic shift a specific register right by 1 SLA (shift left arithmetic) - arithmetic shift a specific register left by 1 SWAP (swap nibbles) - switch upper and lower nibble of a specific register Some of the bitwise ops for rotation were a little difficult, but I was able to troubleshoot. Next is the game ROM, the program counter, prefix instructions, and jump instructions!
Timeliness Rather unfortunately, a lot of work has been done since the last devlog due to yesterday’s hackatime outage. Let’s get started! House of Memories As I mentioned in the last devlog, the next step was to create a program counter (pc, marks where in the program we are) and the memory bus (with all the instructions we have been emulating for the GB to run, and others) Bite the Code The instructions are not stored as plain text of enums, they are stored as bytes , one byte for each combination of instruction and target. I thus have to map each bytecode to each instruction that I have added so far. Not English Class! Anything but English! There are much more than 256 different bytecodes; there are actually nearly 512. To accomplish this and still have memory be 1 byte, there is a bytecode prefix , 0xcb, which tells the chip that the real instruction is the next one, and to look it up on a different table. PCMR To go through each instruction, I added a step function: fn step ( & mut self ) { let ( instruction_byte , prefixed ) = { let temp_byte = self .bus .read_byte ( self .pc ); if temp_byte == 0xcb { ( self .bus .read_byte ( self .pc + 1 ), true ) } else { ( temp_byte , false ) } }; let next_pc = if let Some ( instruction ) = Instruction :: from_byte ( instruction_byte , prefixed ) { self .execute ( instruction ) } else { panic! ( "Unknown instruction found for: 0x{:x}" , instruction_byte ); }; self .pc = next_pc ; } As you can see, execute returns the next pc value, something it didn’t do before. So, I had to go and make all the match arms blocks that do their associated action and also return self.pc.wrapping_add(1) for non-0xcb functions and self.pc.wrapping_add(2) for 0xcb functions, since no function that modifies the pc has been added yet. Next is added yet more instructions! See y’all next time!
Loading Jump Time Hello everyone! I lost track of time, and it appears I have once more logged too many hours with no devlog, yet no excuses I have. Let’s get right to it! HL and D8 A lot of Instructions that I had already made have an HL target, in which they use the value in the joint register HL as an address in memory, and perform the operation on that byte. Up until this point, all HL targets were todos, but, since I had created the memory bus, I was able to quickly wire up those targets. A lot of those same instructions could also take the next byte in memory as the input (D8) so I wired those up too (though before HL). I actually wasn’t sure whether HL targets used a different memory bus than the one used for operations. JP Morgan The next instruction to deal with was JP (jump). Based on different conditions (the state of the zero flag or carry flag, or always) it would or wouldn’t jump to the address encoded over the next two bytes. Probably one of the easier instructions I’ve made. Loading… After JP was load. LD just copies data from one place to another. There are so many variations that I can’t talk about all of them, and there my code is too long for this devlog, so: Byte: Load a byte from point a to point b Word: just like the Byte type except with 16-bit valuesAFromIndirect: load the A register with the contents from a value from a memory location whose address is stored in some locationIndirectFromA: load a memory location whose address is stored in some location with the contents of the A registerAFromByteAddress: Just like AFromIndirect except the memory address is some address in the very last byte of memory.ByteAddressFromA: Just like IndirectFromA except the memory address is some address in the very last byte of memory. This is the opcode table I’ve been using
Pushpops Hello everyone! This time, I actually logged a normal amount of time! Let’s dive in! Loading Shipments I finally finished up the LD instruction! once I got through the weird targets, everything was smooth sailing. Stack Overflow Once LD was done, it was time to implement the stack. If you don’t know what (a/the) stack is, reference the wikipedia screenshot. For the GB, the two methods are PUSH and POP, and the stack actually goes down in memory from the end of the memory bus rather than from the beginning. Dear Functional Bros When I finished implementing the stack, which I though was going to be a lot harder, it was time to add functions to it. This was pretty simple: a CALL instr. that jumps to a point and puts the place where it left off on the stack and a RET instr. that goes to the last location on the stack.
Miscellany Hello everyone! Let’s keep this quick because I have much tedium to get through. What the Hecc is JR?! I finished most of instructions, so I needed to figure out which ones I forgot. I was looking at the opcode table and I realised I had no idea what JR or DAA did, so I had to figure out what they did. JR is just a relative jump rather than an absolute one, and DAA does some number formatting after an arithmetic op for better display. Let’s Table this Discussion Since the instructions that I can implement right now (very nearly all of them, except for stop and interrupts, etc.), it’s been time to update the Instruction::from_byte() fn with all the new instructions. I’m still doing this, and it is purely looking at the opcode table, entering something, looking at the opcode table again, looking at what I wrote, and repeating this over and over and over again. I’m not even done with this! Anyway, enjoy a hashmap meme that doesn’t apply to me b/c I am using a match
Where are my keys? Hello everyone! We are back with a little news, so let’s dive right in! Hashmap, was not, in fact, the solution The mapping from opcode to instruction enum (fn from_byte()) is finally done. Not much to say except that it was a very tedious process. While working, I realised I hadn’t implemented ADD SP, i8 (addassign a signed integer to the stack pointer) yet, so I quickly added that. Unfortunately, that’s all folks! Next, I’m going to start dealing with graphics.
Ticking Time Bomb Hello everyone! It seems that, yet again, I have forgotten to make a devlog for a few hours, so let’s dive in! Fortunately, not much has happened. 16 GB of VRAM As I said in my last devlog, it was time to get started on graphical stuff. I set up a GPU struct with a separate VRAM, and set up the memory to write to that VRAM instead of the regular bus when it’s supposed to. This was also the last part of the docs I’ve been following, so I’ve been on my own since this point. It’s a little pesky that my best resources on how the GB works are now Claude Code and a random opcode table, but what can you do? Tick-Tock Goes the Clock Different operations in the GB’s processor take different numbers of clock cycles to do, so I had to add this as a second return value to execute to add slowing in the future. This was the majority of the hours I logged, staring at the opcode table and trying to figure out the patterns in the timings. Anyway, see y’all next time! These are some silly things that will probably land for at least some of you.
Interrupting Cow Hello everyone! This took a lot longer than I expected due to a large amount of me getting confused by how interrupts work. Let’s dive in! Halt! After dealing with tick returns, the next step was dealing with interrupts. There are two bytes in memory that concern interrupts. One is IE (0xffff), which tells which interrupts to listen for, and the other is IF (0xff0f), which tells which interrupts are currently pending. Dealing with these was easy enough, but there was another thing that needed addressing: The Halt Bug The halt bug is a known bug that, should Halt be ran when there are interrupts pending and the IME (interrupt master enable I think) is off, it doesn’t halt and the next instruction runs twice. I was both confused by how this behaved and how it was triggered, which caused issues with my code that had to be addressed. I wasn’t able to deal with anything besides interrupt handling , so that’s all for now!
Div(isions of time) Hello everyone! This time, I actually remembered to make a devlog! Let’s dive in! Div(vy up Labour) Remember how I added the number of ticks each instruction took as a return value from the execute() fn? Well, it actually got put to use now! There is an internal 16-bit timer in the Sharp SM83, the upper 8 bits of which are exposed at memory address 0xff04. Programming this was pretty easy once I realised I could have read_byte() and write_byte() directly access the counter (Originally, I was going to have an update_div thingy probably and have the bte in memory and the accumulator be separate rather than this simpler approach). pub fn read_byte ( & self , address : u16 ) -> u8 { let address = address as usize ; match address { VRAM_BEGIN ..= VRAM_END => self .gpu .read_vram ( address - VRAM_BEGIN ), 0xff04 => ( self .div_accumulator >> 8 ) as u8 , _ => self .memory [ address ], } } pub fn write_byte ( & mut self , address : u16 , byte : u8 ) { let address = address as usize ; match address { VRAM_BEGIN ..= VRAM_END => self .gpu .write_vram ( address - VRAM_BEGIN , byte ), // Any write to div resets it and its accumulator 0xff04 => self .div_accumulator = 0 , _ => self .memory [ address ] = byte , }; } Now, last time, I set up interrupts, so with div set up, it was time to set up timer interrupts. TIMA Time What actually triggers timer interrupts, rather than div, is a separate timer called TIMA (0xff05 in memory). TIMA has settings set in TAC (0xff07 in memory). Bits 0 and 1 of TAC dictate which bit of the DIV accumulator (let’s call it divacc) to watch to see when TIMA should be incremented. Bit 2 dictates whether TIMA should be incremented at all. TIMA gets incremented whenever the indicated bit of divacc falls from 1 → 0. This can sometimes be as few as every 16 ticks, and since instructions can take up to 24 ticks, this can cause the bit to go from 1 → 0 → 1, which means that you can’t just check the new divacc against the old one to see whether the bit fell, though I tried this as a temporary solution. This is what I had to do instead (see screenshot). I wish it could be prettier, but what can you do? Anyways, y’all, see you next time!
No More GPU Crisis Hello everyone! It seems that, yet again, time has slipped away from me! Let’s dive back in! STATistics When the chip swaps rendering modes, depending on whether the STAT register enables that specific interrupt, it will trigger a STAT interrupt (priority level 2). I had to set up a loop to shift modes so that each mode can easily be fully handled (see screenshot). This was just a matter of finding out how long each mode and line lasted, relatively simple. Render Engines After this, I had to start adding rendering features. There are three components to the rendering: Background Window Sprites The first, and easiest part, was the background. Which tiles compose the background is dictated by a series of bytes after either 0x9800 or 0x9c00, which have references to tiles in memory. Pretty simple. fn render_to_buffer ( & mut self ) { for row in 0 .. 144 { for column in 0 .. 160 { self .gpu .set_pixel ( row , column , self .bgp_palette_lookup ( self .pixel_value ( column , row )), ); } } } fn pixel_value ( & self , screen_x : u8 , screen_y : u8 ) -> TilePixelValue { let map_y = self .read_byte ( 0xff42 ) .wrapping_add ( screen_y ); let map_x = self .read_byte ( 0xff43 ) .wrapping_add ( screen_x ); let tile_map_index = ( map_y / 8 ) as u16 * 32 + ( map_x / 8 ) as u16 ; let tile_map_base = if ( self .read_byte ( 0xff40 ) >> 3 ) & 0x1 != 0 { 0x9c00 } else { 0x9800 }; let tile : & Tile = & self .index_to_tile ( self .read_byte ( tile_map_base + tile_map_index )); tile [( map_y % 8 ) as usize ][( map_x % 8 ) as usize ] } fn index_to_tile ( & self , index : u8 ) -> Tile { let unsigned = ( self .read_byte ( 0xff40 ) >> 4 ) & 0x1 != 0 ; if unsigned { return self .gpu.tile_set [ index as usize ]; } let signed_index = ( 256 + ( index as i8 as i16 )) as usize ; self .gpu.tile_set [ signed_index ] } fn bgp_palette_lookup ( & self , value : TilePixelValue ) -> PixelValue { let index = value as u8 ; match ( self .read_byte ( 0xff47 ) >> ( index * 2 )) & 0b11 { 0 => PixelValue :: White , 1 => PixelValue :: LightGray , 2 => PixelValue :: DarkGray , 3 => PixelValue :: Black , _ => panic! ( "Unknown shade" ), } } The next step is the window, then the sprites. Sprites will be hard due to their varied size. See y’all next time!
Ogres are like Onions Hello everyone! This time, I’ve fortunately logged a normal amount of time. Let’s dive in! Close the Window! After the background rendering was complete, it was time to work on the next layer: the window. The window is exactly as it sounds — the layer between sprites and the background that can have ui and stuff. The window starts at a position dictated by wx and wy (0xff4b and 0xff4a in memory), and those two dictate the top-left corner of the window layer (though wx - 7 is the actual top-left x coord). If a game wants to have a window layer that ends, it will switch off the window layer BETWEEN scanlines, which my current implementation cannot do because I render the whole frame at VBlank (after all scanlines) for simplicity, and it’s not strictly necessary. The code implementation of the window layer was relatively similar to background: fn window_active_for_pixel ( & self , screen_x : u8 , screen_y : u8 ) -> bool { if ( self .read_byte ( 0xff40 ) >> 5 ) & 0x1 == 0 { return false ; } let wy = self .read_byte ( 0xff4a ); let wx = self .read_byte ( 0xff4b ); if screen_y >= wy && screen_x >= wx .saturating_sub ( 7 ) { return true ; } false } fn window_pixel_value ( & self , screen_x : u8 , screen_y : u8 ) -> TilePixelValue { let window_y = screen_y - self .read_byte ( 0xff4a ); let window_x = screen_x + 7 - self .read_byte ( 0xff4b ); let window_tile_map_index = ( window_y / 8 ) as u16 * 32 + ( window_x / 8 ) as u16 ; let window_tile_map_base = if ( self .read_byte ( 0xff40 ) >> 6 ) & 0x1 == 0 { 0x9800 } else { 0x9c00 }; let tile = & self .index_to_tile ( self .read_byte ( window_tile_map_base + window_tile_map_index )); tile [( window_y % 8 ) as usize ][( window_x % 8 ) as usize ] } The next step is the final layer: Sprites! Sprites will be more involved and unique compared to background and window. I still can’t believe the Sharp SM83 did all this for developers. See y’all next time!
Crack Open a Sprite Hello everyone! This dvelopment took longer than expected, so let’s just dive right in! County Rogues, Bring me lOAM After I finished the window layer, it was time to deal with sprites. Where each sprite is on the screen, which tiles to use, and sprite-specific render attributes are stored in a section of memory called OAM. 40 sprites’ data can be stored in OAM, each 4 bytes: the x position, y position, which tile(s) to use, and the attributes of that sprite (height, when to render, etc.) Unlike the background and window, sprites have a lot more moving parts. Here’s my code for rendering the sprites to the buffer. fn render_sprites ( & mut self ) { let lcdc = self .read_byte ( 0xff40 ); if lcdc >> 1 & 0x1 == 0 { return ; } let mut indexes = ( 0 .. 40 ) .collect :: < Vec < u16 >> (); indexes .sort_unstable_by_key (| n | ( self .read_byte ( 0xfe01 + n * 4 ) as i16 - 8 , * n )); let indexes : Vec <& u16 > = indexes .iter () .rev () .collect (); let height = if lcdc >> 2 & 0x1 == 0 { 8 } else { 16 }; for n in indexes { let entry = 0xfe00 + n * 4 ; let sprite_y = self .read_byte ( entry ) as i16 - 16 ; let sprite_x = self .read_byte ( entry + 0x1 ) as i16 - 8 ; let tile_index = self .read_byte ( entry + 0x2 ); let attributes = self .read_byte ( entry + 0x3 ); let palette = if ( attributes >> 4 ) & 0x1 == 0 { 0xff48 } else { 0xff49 }; for row in sprite_y .clamp ( 0 , 144 ) as u8 .. ( sprite_y + height ) .clamp ( 0 , 144 ) as u8 { for column in sprite_x .clamp ( 0 , 160 ) as u8 .. ( sprite_x + 8 ) .clamp ( 0 , 160 ) as u8 { if ( attributes >> 7 ) & 0x1 == 1 && self .bit_7_check ( row , column ) { continue ; } let row_in_sprite = if ( attributes >> 6 ) & 0x1 == 0 { ( row as i16 - sprite_y ) as usize } else { height as usize - ( row as i16 - sprite_y ) as usize - 1 }; let column_in_sprite = if ( attributes >> 5 ) & 0x1 == 0 { ( column as i16 - sprite_x ) as usize } else { 7 - ( column as i16 - sprite_x ) as usize }; let which_tile = if row_in_sprite < 8 { ( 0 , 0 ) } else { ( 1 , 8 ) }; let pixel = self .index_to_tile_unsigned ( tile_index + which_tile .0 ) [ row_in_sprite - which_tile .1 ][ column_in_sprite ]; if pixel == TilePixelValue :: Zero { continue ; } let value = self .palette_lookup ( pixel , palette ); self .gpu .set_pixel ( row , column , value ); } } } } fn bit_7_check ( & self , row : u8 , column : u8 ) -> bool { if self .window_active_for_pixel ( column , row ) { if self .window_pixel_value ( column , row ) == TilePixelValue :: Zero { return false ; } return true ; } if self .background_pixel_value ( column , row ) == TilePixelValue :: Zero { return false ; } true } This also excludes any boilerplate for window and background that I had to reuse. The most annoying part was dealing with ordering indexes efficiently, because the x value of the sprite is used as the method for sprite precedence over layers. I didn’t realise that sort_unstable_by_key dealt with like tuples and you could use that for precedence and tiebreaking. Anyway, that’s all folks! see y’all next time!
Building Buttons Hi everyone!! It seems that time has slipped away from me yet again, so let’s dive straight into your scheduled content! Pushy Pushy Buttons After dealing with sprites (oh! the horror! the suffering! the despair!), it was time to deal with a new kind of beast: the joypad. Implementing joypad functionality meant it was time to make a frontend (ew) instead of just building subsystems and cobbling them together later. This meant it was time to learn stuff about the crossterm crate and make some constructor functions. The screenshot is my current struct for storing the button push data. It used to be 8 bools, but that was 7 bytes too many, so it’s just a u8 wrapper now. Cross-multiply Those Terms! Crossterm is a crate for dealing with terminal keyboard interrupt and such. The first and only thing I’ve done so far was make a wrapper with a custom destructor for Keyboard enhancement flags. struct TerminalGuard { enhancement_pushed : bool , } impl TerminalGuard { fn new () -> std :: io :: Result < Self > { enable_raw_mode () ? ; let enhancement_pushed = supports_keyboard_enhancement () .unwrap_or ( false ); if enhancement_pushed { execute! ( stdout (), PushKeyboardEnhancementFlags ( KeyboardEnhancementFlags :: REPORT_EVENT_TYPES ) ) ? ; } Ok ( Self { enhancement_pushed }) } } impl Drop for TerminalGuard { fn drop ( & mut self ) { if self .enhancement_pushed { let _ = execute! ( stdout (), PopKeyboardEnhancementFlags ); } let _ = disable_raw_mode (); } } Computah, Make me a New Computah Since it was time to write a main() fn, it meant I also had to write constructor functions for CPU and all it’s subsidiaries, since I’ve not bothered to do it before. That’s it folks! See y’all next time!
You’ve Been Selected for a “Random” Screening Hello Everyone! We are back! I don’t know how it’s already been over 4½ hours! Let’s dive straight into the progress! They Know How to Push Each Other’s Buttons After making constructors for the structs and refactoring the button data struct, I added a loop to handle all waiting button presses and set the data in the button data struct. Not particularly complicated, though it was fun to test something that actually did something. Staaaaahhp Instructions take a set amount of time, so it is necessary to have a pause, since even a TI-84 (buy the CG-50 or HP Prime G2 instead; don’t get the CG-100, it lacks plugin support, which is why I recommend the CG-50 at all) is faster. Since systems can’t pause for a single tick, it pauses frame-by-frame instead. How to Exit Neovim I didn’t take the time to add a break condition, so I added a check to see if the escape key had been pressed, and if it had, it quit the application. Show Me the Money The biggest development was the render loop. Now, there is actually a visual to see and play with. Paint a Pretty Picture I also felt it would be boring if there was only one colour theme, so I added an enum along with PixelValue => Colour matching so that you can pick a theme! Speaking of picking… Staring at a Config File Since the user has to be able to pick a theme, I felt it would be good to have config.toml support for ease of use.
Human Error Hello everyone! I got too much in the flow and in the RAGE, which is why I logged nearly 8 hours since the last devlog! Let’s get in the weeds! Loading Bay I added ROM loading!!!!! I can actually load a test ROM in now!!! Naturally, I immediately loaded one in, and that brings us to our first issue. Speed? Issues When I was loading in a test ROM, the text of the name of the test would peek from the bottom of the screen, then jump up and not show passed. I probably spent 2 HOURS on trying to fix this. I thought it was a speed issue, and I ended up making a ton of optimisations and added alternate screen switching. BUT GUESS WHAT??? I zoomed out my terminal window, and there it was: Passed. I would share a screenshot, but I am currently working on changes to load_rom() that break the emulator currently (bank switching). Don’t Stop I mentioned a while ago that I had to emulate a bug in the hardware that caused the next instruction to occur twice if HALT is called when there is an interrupt pending and IME (interrupt master enable) is off. My implementation caused it to infinitely stall instead. So I fixed that bit, but the HALT bug test rom still wasn’t running the tests, so I found that LY couldn’t be read from memory, so fixing that fixed the thing, allowing the tests to run and FAIL. So I had to run a disassembler on the HALT bug test to find the exact mechanics of the HALT bug, then write it. Even though the values for the result were correct, it still wouldn’t pass. This was because of an oversight on my part when I was writing the interrupt handling. The IF byte (0xff0f, pending interrupts, whether or not they’re enabled) in hardware always has its highest 3 bits as 1, and I didn’t think it was necesary to implement this when I was writing interrupt handling, so I ignored it. And finally, fixing that, the HALT bug worked. The screenshots are: Gambatte failing the test. mGBA passing. RustBoy (May change to GameGirl/RustGirl for uniqueness and searchability) passing. See y’all next time!
You can switch the banks, switch banks, swi- swi- swi- swi- swi- — until you’re full of life Hello everyone! I changed the name of my project directory, so I wasn’t seeing my hackatime or had the ability to write a devlog until I realised the problem, so let’s just dive right in! Bank It The next thing to deal with was bank switching and better behaviour for handling roms. Cartridges are physical objects, and thus can have extra RAM and storage for the gameboy. These are controlled by MBCs (memory bank controllers). The most common ones are MBC1, MBC2, MBC3, and MBC5. For this project, for now, I am only emulating 1, 2, and 3. In the future, I will probably add more controllers once the full architecture is done for at least these three so I have an idea of hwo the skeletons should look. The first step of bank switching was having the rom portion of the memory be read-only and writes to it do certain mbc actions, like enable ram or switch banks or switch ram. I am currently implementing the switching of banks. I need to get back to MBC1-specific flags and writes. See y’all next time!
RAM or ROM Hello everyone! I’ve finally logged a normal amount of time again! Let’s dive right in! MBC Say One! I managed to bang out my first supported MBC (memory bank controller), a lot of it done while walking on the treadmill this morning. I had already implemented the ROM bank switching by this morning, so next I had to implement switching RAM banks, which has some pretty funky indexing. Grace Hopper Naturally, what does one do when they have a hopefully working MBC system, you try a rom!. I only found out tetris doesn’t use an mbc after removing my basic mbc substitute stuff that enabled me to run simple roms. Speaking of Tetris, the screenshots are me attempting to run tetris iwth different themes (the themes work, tetris doesn’t which we’ll get to). The themes in order Wisteria GBGRreen Jungle Developer (Don’t worry, there’s more themes, and you can always submit an issue or PR with a new theme) Let’s get back to BUGSSSS!! When I tried to run tetris, it wasn’t accepting input and the arrow showing which options are selected was unpresent. These issues are the next things to resolve. I found that there was a critical error in the read methods on my button data stuct Here is the commit with the changes in case you are curious. I found out that there is a spot in memory you can write to to load a sprite from some address into OAM. This is the next thing I will be implementing before I try finding the next issue with input so I can at least see some pretty sprites. Anyway, see y’all tomorrow after I hopefully get tetris working!
Can It Tetris? I say hello to you again, my near and dear viewers, as I begin this devlog with a frustratingly long amount of time logged. Let’s dive in so I can put the last few days behind me. DMing the Memory Last time I menioned that there was a feature I needed to add that allowed the cpu to write a byte to a register in memory to trigger a transfer of data from the address specified by the written byte to the OAM (object attribute memory — sprites and stuff; this transfer is the DMA OAM tranfer) because that was the main way stuff was written to oam b/c otherwise it would be too slow. Originally, I wrote a doubly recursive function kinda thing b/c it called read_byte() when it itself was called indirectly by read_byte() and it also called itself, but I scrapped it due to unguarenteed tail recursion and the slowness of repeatedly matching the section of memory being written to/read from, so I just used copy_within(). This also somehow fixed my input issues, so idk. Double Bug Whammy This is the part of the last few days that has been a slog and a horror. After adding the DMA OAM transfer, I tried running Tetris, and it worked…until I got into gameplay. The sprite showing the selected player amount on the start screen was working and it would respond to user input. However, when it hit actual gameplay, it wouldn’t render the tetromino sprites until they were baked into the background as part of setting in place. To try and diagnose the problem, I ran blarrg’s big cpu_instrs.gb test ROM, and it stopped rendering frames in the middle of the 3rd test, the standalone ROM for which had been working without a flaw. Testing in mGBA through RetroArch (I don’t have mGBA installed standalone on this device), I found that the test takes a second to run, but doesn’t cause the whole emulator to hang. Also, since new frames weren’t being completed, it wouldn’t poll for input since it only polls on a frame completion, which meant I had to close the terminal session to quit the emulator. I spent hours running tests just to find that it was getting hit by a STOP instruction it couldn’t escape. I checked Pan Docs and found there had been some unimplemented STOP behaviours. After implementing those and it still not working, I set STOP to only stop for a single cycle, which let the test ROM pass all the tests with no timing issues. After that, within 2 minutes, I was able to find an incorrect == (should have been !=) in my sprite attributes bit 7 sprite priority control, allowing Tetris to finally work!!! Next up is: MBC2 MBC3 MBC5 Saving Audio TUI rather than bare CLI a lib.rs so others can write a frontend on top of Rusty Game Girl A style guide and making my code compliant (hard to do while writing and over a codebase who’s size is at least 4.5x bigger than anything else I’ve ever worked with) Unit tests also here’s a video of me selling at Tetris b/c I wasn’t listening to Jamie Paige while playing
Clip It Hello comrades! We’re back with yet another devlog! Let’s dive in so I can get back to programming! Memory Control (Not 1984 Style) A bit ago, I mentioned that since cartridges were physical pieces of hardware, they could have logic chips of their own controlling their own RAM, so I would have to emulate those on top of the functionality of the actual GameBoy’s hardware. The next MBC (memory bank controller) to emulate was MBC2. MBC2 is weird. It has ROM banks as standard, but it also has 512 half-bytes of RAM built straight into the MBC chip for some reason. Those were a little weird to emulate, but altogether straightforward. I also had duplicated RAM indexing logic for both reading and writing to the cartridge RAM, which I marked todo in my code, so I had an excuse to do a quick refactor. After handling the weird little freak that is MBC2 was implementing No MBC. You may be asking, V, what’s there to emulate if there is nothing to emulate? And to that I say, it is once again weird and silly, just how we like it. Even if a game has no MBC, it can still have some RAM; it’s just always on and has no logic for accessing it. It ended up with me having to deal with a lil extra changes to handle some things I was not anticipating. Looks Like You’re Trying to Kill a Stick Figure A while ago, I watched a prime video (who was also the source of the idea for this project) about the Power of 10. I’ve since thought these rules are pretty neat and can serve to make code better (with possibly the exception of rule 7). I decided that I wanted to follow rule 10 for my code, so I foolishly set all lints to max pedantic, thinking I would have some lints, but I ENDED UP HAVING HUNDREDS OF MOSTLY USELESS LINTS, many of which ended up being me using as for casting causing less readable behaviour. Some of them were useful, such as me allocating MASSIVE arrays on the stack, even if they were boxed. Turns out Box::new([0; 0x20000]) creates the array on the stack first before memcopying to the heap in unoptimised builds, which is completely unnacceptable. Also, may of my other arrays were just straight up massive and unboxed, like the framebuffer or bus memory flat array. Anyways, after spending the bulk of this devlog’s time dealing with that, I gave up knowing the rest of the lints were harmless and pointless and clogging up important lints. In other news, despite listening to Jamie Paige for many hours this weekend, I still ended up needing my fren to tell me about it’s new song with Vane Lily and Yuma . I also showed them to Developer theme and it brought them some joy. I’m also very happy I got the thermometer question right, especially since I have an infrared thermometer on my desk and I had to find the correct emissivity setting to use on milk yesterday since my dad was making yoghurt. Anyway, I’ll end this before Grammarly shows me any more lints for this devlog.
Third Time’s the Charm Hello everyone! I would have devlogged sooner, but I’ve spent most of the last time between this devlog and the last on a single feature, and the rest on YouTube. Let’s dive right in! Clock Iiiiit! After adding MBC2 and No MBC, the natural next step was MBC3. MBC3 is a lot like MBC1. It uses mostly the same register, many of which are direct copies or basically the same. There are only two key differences: MBC3 supports more RAM and has an RTC (real time clock). The more RAM wasn’t much to deal with, but the RTC kinda was. I messed up a good deal of the ticking on the irst pass of writing it, and I kept forgetting how if let worked, which did end up slowing me down. Anyway, it’s well past midnight and I need to smash my face in my pillow. My one regret is that I didn’t listen to enough Jamie Paige while devving.
Rap It Up Hello friends! We’re back with the Daily Devlog! This one’s a quickie because the docs were (mostly) clear and I was listening to Rob Apollo while working. Let’s dive right in so I can run a mile on 5.5 hours of sleep rather than only 5! The Final Boss of MBCs When I started to do MBCs, the scope of this project was to do no MBC, MBC1, MBC2, MBC3, and maybe MBC5. I have been completing them largely in sequence: MBC1 MBC2 No MBC MBC3 (yesterday) This leaves only MBC5, today’s feature. I thought MBC5 was going to be difficult, but it was probably the easiest MBC besides no MBC, which isn’t even an MBC! MBC5 is basically a simpler and bigger version of MBC1, with two exceptions: Rumble: Apparently, cartridges can shake it down because some MBC5 variants have a rumble motor controlled by bit 3 of the CRAM selector. If there is rumble, that bit is dead. Chunks Every other MBC I’ve implemented followed this general rule: the ROM is exposed of 0x8000 bytes. Those bytes are split into four sections, each of 0x2000 bytes. Depending on which register you wrote to, it would be a different MBC command/
SAAAAAAAVVVEEE MEEEEEEE Hello everyone, we’re back with our daily devlog, this time before midnight! Let’s dive in! I’ll never forget you After finishing the MBCs, it was time to work on saving the game state. I had to save the data in binary form. Originally, I was going to use bincode, but I switched to postcard since bincode has been deprecated. I managed to get the plumbing working, so now I just have to implement the saving in main.rs. I had to keep this short since I need to go to bed. See y’all!
Frontman Hello everyone! This will probably, maybe, be the shortest amount of time for a devlog, so let’s hop to it! I’ll never let you go Last night, I implemented the backend funtionality and started on frontend functionality, a ’lil bit. Over the past 30 odd minutes, I finished up the saver struct. I wanted to have Saver be have a custom drop impl, but that would require it to carry an immutable reference to the CPU through the running of the emulator, which would break ownership rules due to having to make a mutable reference to the CPU to step it. What I did was I isolated the loop into a function and saved the return value to a variable, which gets propogated after the emulator attempts to save. The video is of me loading in a save into Pokémon Red! Comment which theme you want me to show off next!
Blink Twice Hello everyone! This is going to be a shorter devlog, so let’s dive right in! NOOOOOOOOOOOooooooooo When I set up the keypress handling a bit back, I wanted to use the default Retroarch keybinds so that anyone who had ever used Retroarch could use RGG without difficulty. The one discrepancy was that you only had to click escape one singular time to quit the emulator. I am no stranger to mistaken clicks, and today I realised this could be an issue for anyone who shares some of my clumsy tendencies. I figured it iwould be good to better copy Retroarch’s binds by making it so that you had to click Esc twice in quick succession to quit. A nice and easy change tha’s completion will probably force me to actually stop procrastinating audio/APU. Anyyywayyy, y’all should listen to CityLimits , DeltaFey , and Black Polish . I feel all of their music is great for programming. We’ll see if I can post another devlog today. ¡Adiós!
Apu, like from The Simpsons ? Hello beautiful humans! We’re back, and I’ve finally stopped procrastinating on the APU! Let’s dive right in! CPU, GPU, TPU, NPU, LPU, DPU, and now APU?! I’ve been very scared of dealing with the APU, since it’s seemed like the hairiest thing to deal with, and it’s also the last part of the emulator core. The APU has four channels and mixing and fading and stuff I will get into. 2 Chan The first channel I started with was channel 2, since it is the simplest (1 has an extra feature, but otherwise, they’re the same) There are four things one needs to understand for these channels: Period Duty Length Envelope Duty is the waveform of the audio, like what percentage of the square wave is at level 1 vs level 0. This changes the timbre of the sound. Length is the amount of time the note plays for. It’s harder for devs to have to manually turn off the notes on their own b/c of timing. Envelope is the fade in/fade out of the sound Also, on channel 1, there is sweep, which changes envelope I think? I don’t know b/c Pan Docs is confusing for this (built for game developers it feels) Aaaanyways, that’s it from me for now, as I have a history in-class writing test thing tomorrow and I’ve already hit my 2½ hour goal for the day. Also, I was listening to Constant Companions and I realised I probably never listened to the album hard enough b/c “Clouddrop” is a lot more depressing than I remember, and I got ts on vinyl soo that’s something I will be embarrassed to tell my friends. Also, y’all should listen to Constant Companions to increase her listeners so that one day, Jamie can have her Paige again.
Sweep It Under the Rug Hello humans! We’re back with the DAILY DEVLOG!!!!! I need to go to bed and feed my geck, so let’s dive right in! No Sweeping for You! Today, I finished up last night’s work of finishing channel 2. All that was left was adding an output to u8 fn that’s just sitting there as dead code rn and wiring it up to read_byte(). relatively uneventful, except for when I forgot how bitwise operations work. WeeeWoooWeeeWooo After channel 2, the obvious next step was channel 1. Channel 1 is just channel 2 with an extra feature: sweep. Sweep is kinda funky. I thought it changes envelope, but it actually changes pitch. It does some weird funky shenanigans to change the pitch and also on note trigger. A lot of building channel 1 was just making echo fns for proper layering of structs. Pan docs was once again confusing. Notes A side note on documentation. While working, I decided to read the forword of pan docs, and lo and behold, its built for game developers, but there’s also a Complete GB Technical Reference which was supposedly built for emulator builders. I decided to check it out and see if it would be more useful than pan docs. BUT GUESS WHAT?? It had EVRYTHING EXCEPT AUDIO in there!! The word “audio” was used exactly once!! Apu wasn’t even mentioned!!! SO I have to continue with pan docs, which does have decentish detail for audio. Music I ended up listening to a lot of Chappell Roan, Cavetown, CITY LIMITS, and MAFUMAFU this session. Y’all should listen to the PEAK music of Sorry Ghost as well. See y’all next time!
4Chan Hello everyone! Welcome back to the Daily Devlog, where we go over the new developments in RustyGameGirl!! Let’s get started with today’s broadcast! White Noise, Brown Noise, Black Noise, Grey Noise, What’s the Difference? After finishing the two pulse channels, 1 and 2, it was time to start a new one. I went with channel 4 since it is the closest to 1 and 2 in terms of structure and simplicity. Channel 4 is a randomised noise channel thingy, for stuff, like gunfire maybe? Here’s the summary from Pan Docs: This channel is used to output white noise, which is done by randomly switching the amplitude between two levels fairly fast. The frequency can be adjusted in order to make the noise appear “harder” (lower frequency) or “softer” (higher frequency). The random function that switches the output level can also be manipulated. Certain settings can cause the wave to be more regular, sounding closer to a pulse than noise. A lot of channel 4’s machinery is the same as 1 and 2’s, like envelope and length, which brings us to our next section. Adaptive Water Body Until today, I have never written a trait in Rust. I’ve used them, of course, especially Default and Drop, but also the num-traits crate from when I was making my fast-factor crate, but I’ve never written one before. I figured now was as good a time as any, especially since all four channels share many similar methods, like the nrxy reads and writes, output (to u8 only), clock_length, tick_period, etc., so I added the three following traits: trait Channel { fn output ( & self ) -> u8 ; fn tick_period ( & mut self , ticks : u32 ); fn clock_length ( & mut self ); fn nrx1_read ( & self ) -> u8 ; fn nrx2_read ( & self ) -> u8 ; fn nrx3_read ( & self ) -> u8 ; fn nrx4_read ( & self ) -> u8 ; fn nrx1_write ( & mut self , byte : u8 ); fn nrx2_write ( & mut self , byte : u8 ); fn nrx3_write ( & mut self , byte : u8 ); fn nrx4_write ( & mut self , byte : u8 ); } trait ChannelWithEnvelope { fn clock_envelope ( & mut self ); } trait ChannelSpecificFeature { fn nrx0_read ( & self ) -> u8 ; fn nrx0_write ( & mut self , byte : u8 ); } Having all the separate impl blocks forced and ordered the same makes the code far more readable, and it’s not like this will slow the code, and it sped up the creation of channel 4 and will likely speed up channel 3’s creation. Notes Rather unfortunately, since I was coding piecemeal today and I was to lazy (probably b/c of the gudetama shirt I’m wearing), I didn’t start listening to music until ater I was done coding, while I was working on homework (English I think? It was the only real homework I had today since I did most of the rest in class), but a Polyphia song just popped up in my shuffle (Reverie), and I locked in so much harder. Also, once I started working on the APU, I cracked 4000 SLoC!!! That’s more than my last two projects doubled and combined. It’s also likely the majority of code I’ve written in the 7 years I’ve been programming. I am currently at 4509 SLoC, which is over triple my last two projects combined! See y’all tomorrow, if not later tonight!
Madra Channels Here we are; we’re back again, making this a bidaily devlog?! Let’s dive into the last 1½ hours of development so I can touch bed. Me-Chan Navigator After finishing channel 4, it was time to tackle the most complicated (maybe, but 4 is pretty funky), or at least the weirdest. I thought channel 3 varied it’s pitch based on the info in the wave ram, BUT NOOO, it varies the waveform instead. It let’s the gameboy make more varied sounds rather than just variations of beeps, like saw waves and stuff. Lots of its machinery is reused from the other channels, and it also doesn’t have envelope because you can vary the volume yourself. That’s it for this devlog! See y’all tomorrow as I probably will finish audio tomorrow! I don’t think there is an applicable image for this, so have some xkcds
BLAAALAKKLJHSLJHDKHJALKJHLKGSFG Hello everyone! It seems that the devlogs have become oppositely bidaily — just this once, don’t you worry — than my last two devlogs. Let’s dive right in since we have so much to cover. audio output master race After I finished chan 4, it was time to implement master apu registers and channel mixing and panning. Most of the registers were pretty simple, except the one with the master APU power on/power off, as to implement it I had to gate every write on whether it was on, except the wave RAM. I also did that during my PE class (while exercising, of course), which contributed to the difficulty. Fortunately, I write my code in Neovim, so it really sped things up, including 1-handed editing. With that, the backend was nearly done, and I had reached the point at which I would no longer need Pan Docs or anything of the like, just straight Rust sailing. Thread the Needle In order to get the audio timed correctly, I had to hook up tick_channels() to a channel on which it sends a tuple of u16s (left and right amplitudes) to some receiver. This lets the audio be decoupled from whatever frontend one has. Your Pal, CPAL cpal is a a Rust crate for audio and stuff. Here’s the audio stuff because I can’t think straight it’s so late & I have an integration by parts quiz tomorrow and I don’t want to fail something light b/c I didn’t sleep. fn get_audio_stream ( cpu : & mut Cpu ) -> Result < Option < Stream > , Box < dyn std :: error : : Error >> { if true && let Some ( device ) = cpal :: default_host () .default_output_device () { let supported_stream_config = device .default_output_config () ? ; let stream_config = supported_stream_config .config (); let sample_format = supported_stream_config .sample_format (); let ( sender , receiver ) = std :: sync :: mpsc :: sync_channel :: < ( u16 , u16 ) > ( 5000 ); let error_callback = | _err : cpal :: Error | {}; cpu .set_audio_sample_rate ( stream_config .sample_rate ); cpu .set_audio_sample_sender ( sender ); let stream = match sample_format { cpal :: SampleFormat :: F32 => { let data_callback = make_data_callback :: < f32 > ( receiver , stream_config .channels ); let stream = device .build_output_stream ( stream_config , data_callback , error_callback , None , ) ? ; stream .play () ? ; Some ( stream ) } cpal :: SampleFormat :: I16 => { let data_callback = make_data_callback :: < i16 > ( receiver , stream_config .channels ); let stream = device .build_output_stream ( stream_config , data_callback , error_callback , None , ) ? ; stream .play () ? ; Some ( stream ) } cpal :: SampleFormat :: U16 => { let data_callback = make_data_callback :: < u16 > ( receiver , stream_config .channels ); let stream = device .build_output_stream ( stream_config , data_callback , error_callback , None , ) ? ; stream .play () ? ; Some ( stream ) } _ => Err ( "Audio output sample format not supported! Try disabling audio in your config with 'audio = false'" , ) ? , }; return Ok ( stream ); } Ok ( None ) } // makedatacallback not shown due to char limits Position,Velocity, Acceleration, Jerk, Snap, Crackle, and Pop After that, I tested it, but there was some terrible audio crackling. Turns out it was due to oversleeping when trying to time things correctly, though before finding this out, I ended up slightly optimising draw_frame().The artifacts seem to be mostly gone and I will leave this to tomorrow silly. nearly @ 5000 sloc
Scream it Loud and Scream it Proud Hello everyone! We are back with the once again DAILY devlog!!! Let’s dive right in! IT LIIIIIVES!!! This morning, as soon as I came up, I sat my @ down to trouble shoot my audio processing on 4 hours of sleep . Despite adding the manual sleep time reduction, I was still experiencing some crackling, so I found a bug in the channels, solving which didn’t solve anything, but switching timing to be based off of audio when available worked like a charm, giving better and more accuracte speeds and crisp audio, so I was able to show off my COMPLETED EMULATOR CORE to some of my friends (as well as my backpack with Ferris on it). IT DIIIIIES!!! After adding the audio, I had to make sure that it wouldn’t cause the emulator to break, so I added a timeout that shuts off audio if it’s broken for more than 500 ms YES CAAAAAAP Because of how I was translating from u16 audio to f32, I was getting popping, so I implemented a software capacitor to slowly dampen sounds closer to EQUILIBRIUM SWITCH IT UUUUP After audio had all it needed, it was time to work on something I had long anticipated, a lib.rs for people o build their own frontends on top of my emulator. I also added an Emulator wrapper struct around Cpu so that it would be easier to manage pub methods and such without the ~1550 other lines in cpu.rs making things hard. There’s a reason this project isn’t just one big file of 5000 SLoC. I also added some documentation, and I found the best use of Claude Code is checking for spelling errors (it’s like I’m illiterate!) Speaking of SLoC, I cracked 5000 today! This is about 3.5x larger than my last two projects combined! I’ll see y’all next time, and don’t forget to listen to beabadoobee’s new album Pylon and Jamie Paige!
Dripmogging Hello developer theme users! It’s been a couple days since my last devlog, since I couldn’t make one last night since my dad thought I had school today, so let’s dive in so I can work on compliance and becoming a peer tutor for math for my school! Bitmasks: origins One of the biggest parts of this project is working with bytes and getting data from bytes. To do this, you have to shift the bits you want to the end and then mask them like (byte >> shift) & bitmask . I feel like I’ve gone through dozens of different styles for bitmask and shift numeric literals, which is where the whole style guide idea came from. Personal Stylist For the past 4 hours, I’ve been perfecting a style guide for my codebase. It’s a beautiful org doc with that sweet sweet table of contents with rules on everything from numeric literals to tests to error enums. There’s not to much to say except the next step is going over all 5000+ lines of code to enforce compliance. I’d like that written up A quick note on org. Many online code hosting services like GitHub and Codeberg support org rendering. Rather unfortunately, crates.io only supports a README.md, and not a README.org, which gives me a big sad, especially since I’ll haveto rewrite my README in markdown (ew), which does not give me the luxury of an automatically generated table of contents. That’s it for now folks, see y’all tonight!
If You Do Not Comply Hello everyone! We’re back with another devlog! I need to go to school soon, so let’s dive right in! Numerics, Literally Since I made the style guide, it was time to start implementing it. So far, I’ve gone through emulator.rs, memory_bus.rs, apu.rs, and cpu.rs. emulator.rs This one just needed doccomment formatting. memory-bus.rs This one needed a few numeric literal changes. I also directly accesses the stat field of gpu, which is unnacceptable, but since I’m doing gpu.rs next, I left it for the time being. apu.rs Despite being the file in which I came up with a chunk of my numeric literal style while writing, it felt like every literal was wrong. cpu.rs This was the second easiest file, despite it being the longest by a wide margin. There was some repetitive numeric literal issues in the methods, but that was it. I also tried to split it into multiple files for length since it is double the size of any other file, but I lowkey failed. Next up is gpu.rs compliance. I’ll see y’all next time!
You Will Be Terminated Hello everyone! We’re back with another devlog! Let’s dive right in since it is 02:00 over here! Lookin’ So Fab I have contined with my quest to implement my BEAUTIFUL and living style guide. Here’s what I did, since this work is mostly just repetitive and long. gpu.rs and memory_bs.rs publicity compliance. Before today, 4/6 of Gpu’s fields were pub(crate) something they had no business being, so I refactored so everything uses methods. Took a chunk of time but it was very worth it. registers.rs This one was pretty stupid. It had two main issues, publicity and masks. The lackluster and incomplete guide I was following had me write registers.rs with a ton of bitmasks it didn’t need it in a way that was ugly and sad, so I dropped the masks. All of Registers’ field and subfields are pub(crate), which I determined is optimal because it would be a major and massive chang to Cpu, and the registers are purely for the Cpu to use, and are part of the Cpu. rom.rs rom.rs was pretty chill, only a few things here and there. joypad.rs Despite this one taking a matter of minutes, this one is going to cause my so much work. All of its publicity was correct, as well as bit selecting formatting. Turns out I’ve been forgetting to remove parentheses around my files! Simply ridiculous! ANyway, that’s all for now, since I need to go touch bed. So y’all later, and keep listening to Jamie Paige!
Comply-cations Hello everyone!! We’re back with another devlog today. Let’s dive right in so I can start the next step! Parensal Issues As I said in my last devlog, while working on compliance for joypad.rs, I realised that I needed to delete extra parentheses around operators with obvious precedence. After working on that file, I continued down the buffers open in Neovim, also correcting that issue in addition to the others. Because I only caught it midway through, I have to complete a second pass of compliance editing. Fortunately, I have mini.surround installed, so deleting parentheses is really easy. That’s all that really happened. Make sure to listen to the daily dose of Jamie Paige and Vane Lily’s new song!
Ratatouille Hello everyone, we’re back with another devlog! Let’s dive right in so I can hit bed so I can go rafting the day after tomorrow! Compliance Chips I’ve finally finished dealing with compliance! I’m surprised by how many extranneous parentheses I had! I knew I had a lot, but I didn’t know it was this much! I’m so happy that is over so I can get to real work! Tui T. Sutherland The last big feature I wanted to add before shipping was a TUI for selecting ROMs and saves. To do this, I need to use the ratatui crate, which fortunately run off the same crossterm my project already does. So far, all I’ve done is learn about it and write the fn for getting the directories/files with the appropriate endings and sorting them for display use. I’ll see y’all in a couple days!
Pick Me Hello everyone! Let’s dive in so I can go feed my gecko and sleep! Seleccionar For the past few hours, I’ve been working on the file picker for selecting a rom and a save file. A lot of it is just learning about how to use Ratatui. Originally, I was going to have the selecting happen all at once, by one function, but I switched it to two calls so that load_rom() and has_battery() can be called to check whether the save menu should even come up. Write-In Candidate The thing is, since with a new save, the file has to be constructed by the emulator and it doesn’t exist. Right now, I’m writing a little text input field to be triggered if the save menu gets close with Esc. This will be pretty short, and won’t require a large change to existing code. After that, I’ll wire it in and test. Next Steps Homebrew Cask Separating frontend from backend crate by crate, so lib users don’t have to deal with frontend only imports.
Vendetta Hello everyone! Let’s dive right in so I can work on my math homework so I don’t fail my calc test next week! TUIzers I COMPLETED THE TUIIIIII!!! I added a text entry box for people to manually enter the names of their new saves. I also stylised the highlighting a bit more, and , even though its not in the video, now, the color changes to green when you’re selecting a save. This was a really fun learning experience. I originally wanted to make a TUI because one of the raters on my last project, leading zero util , complained about having to use a simple CLI and told me it wasn’t that good because of the lack of TUI, so this has been kind of a personal vendetta against that person to improve my software and user experience. The Other Interface This project isn’t just a CLI utility. The entire time I’ve been building this, I’ve wanted to enable others to make a frontend for it, whether it be on the web or a gui or some port of it in something. I’ve maintained a careful separation between lib and bin, with the exception that they are the same crate. For library users, dealing with my errors would be impossible since many of them are just strings and they are all just Box. Before throwing this on crates.io, I want to make error enums and stuff so people can do error matching. Tap Tap After completing error enums and stuff, I’m going to throw it on crates.io and make a github release so I can make a Homebrew tap. The last time I made a homebrew tap, I gave the project it’s own separate tap repo, but this time I’ll keep all my taps in a new tap repo on my GitHub instead of Codeberg for users. That’s it for now! I’m excited to make error enums so I can have that skill under my belt as well! Also, I’ve reached the point where shipping would probably let me get a framework 12, which is very exciting for me.
Erroneous Hello everyone! I have a history in-class-writing in 6 hours, so let’s dive right in! Error 404 Once I started adding support for loading ROMs and saves, failure points became possible. I hadn’t previously written errors before, so I just wrote strings and propagated them through with Result< T, Box> and the ? operator, with no regard to API users that may want to recover from a failure. I’ve been putting this off for so long, and was even considering doing this after shipping, but since I have so much time on my hands and the only other big TODOs left are the README.md and the Homebrew tap, I decided, why not? This has been a very interesting process over all, and it even gave me the impetus to do some proper gating on load_rom. Different MBCs only support some cram sizes, but because I wasn’t gating on load, these would fail at runtime with a mismatch. Read All About It Next up is the README. I previously had a mediocre, small, incomplete README in a file formet I forgot crates.io doesn’t have a renderer for, so I figured it would be best to start from scratch in Markdown instead of trying to port it over from Org. I also chnaged the name to Riggurly since that rolls off the tongue more. That’s it for this morning! I also found the bathroom in ENA: Dream BBQ, which was cool. I still need to go inside. Also, make sure to listen to Jamie Paige, as she always boosts my productivity!
Lover Tell Me if You CanHello everyone! Let’s dive in so I can work on improving the user experience!## ManThis devlog is all about README. I have actually never used Markdown before, only org, Emacs’s outliner/md format, because it is more intuitive and Emacs gives you more toys. My style guide is in org, my todo lists are in org; the only things that aren’t are the AGENTS.md I stole from Ghostty and the README.md. I ended up using a combination of the Stardance and Phantom README guides to help me figure out what to put in it and how to do syntax. Working on the README put an emphasis on some flaws in my code:- config path ~ not expanding- my glaring lack of a homebrew tap- mono audio still being unsupported- feature gating on binary-only depsThat’s it for now folks!
Much TODO About Nothing Hello everyone! We are finally back after a devlog hiatus due to me having to write essays to apply for my school’s software team! Let’s dive in so I can work on my history homework! Read It and Weep Last time, I talked about, in one title line by accident, how writing documentation and documenting my software’s flaws really exposed what was wrong with it. This update is all about fixing those flaws (mostly) A Multitude of Screams One of the biggest issues with my emulator was the audio. Not in the backend, but the frontend. Due to laziness, I didn’t bother adding mono audio support, which ended up being a 5-minute fix. There was also another glaring issue with the audio: Once it crashed, it wouldn’t rebuild the stream. If you unplugged your headphones, thinking the audio would continue on your other speakers, it would just turn it off and modulate speed with std:🧵 :sleep() . It took me a while to write all the rebuilding code in such a way to maximise user QoL for all users. When did I download you?! Since Riggurly is both a CLI AND a library, it has dependencies that are used only in the CLI that are still a part of the package as a whole. Library users still have to download and compile these dependencies, which can be a hassle and a waste of time. I’ve been meaning to put CLI-only dependencies behind a feature so library users can easily prevent themselves from downloading and compiling a bunch of heavy extra dependencies. This also taught me how to use features, which was good. You’re a Waste of the Air You Breathe! One thing that’s been bugging me for a while is efficiency. My array for the fallback memory bus array in the memory bus struct has always been 0x10000 bytes long (1 mebibyte, how much ram a GB has (kind of)). How reading and writing has worked is that there are match statements you go through to see if you access that array or you access the rom or if you access the vram or something else. The problem is that over 3/4 of that array’s length is useless and shadowed by ROM/CRAM/VRAM. I was wasting 768 kb of heap-memory. I ended up dropping the first 3/4 and just having a smaller array. Unfortunately, some space is still wasted, but it’s not much compared to before. I showed @ElffawIThink and they though it was funny. Also, one of the ROM types comes with 512 HALF-BYTES(nibbles) of RAM, which I had been representing with a [u8,512] with the upper nibbles of EVERY BYTE going unused. I shrank this down to a [u8,256] and use both nibbles for a fish in the sea . Take Me Home Before, I had to put a disclaimer in my README that you couldn’t use ~ as your home directory in your default ROM path in your config.toml because different users have different home directories and I have to use the dirs crate to find that out at runtime. This was a surprisingly long fix, though it was quite simple. Just a few lines of code to strip the ~ and replace it with the already found home directory. That’s all for tonight comrades! Make sure to listen to Jamie Paige, especially Clover! That song is now the first song in my playlist with vocal (sequentially, obviously my playlist has other songs with vocals), overtaking Lemon Boy by Cavetown, it’s just that good. It has also shown itself to be a better daily anthem than Villain by Teniwoha and Schifty Five, the YouTube classic.
Little Bonuses Hello Everyone! I have to go walk the dogs, so let’s dive right in! Building Codes Since I want a few more hours to try to see if I can get a Framework 13 and Celeste, I decided it would be good to add all the nicities I did for my last CLI, leading-zero-util (I know, terribly named), namely, man file and completion generation. On lzu, I used a hidden flag for completions and had a build.rs for the man page. However, I found it best to merge all of them into one hidden flag for ease of use. At first I tried a build.rs, but it kinda made dependencies annoying and made either my or lib users’ lives worse. That’s it for now folks! Next I’ll be working on unit tests for ROM stuff.
sleep(); sleep(); sleep(); allTestsPass() Hello everyone! Let’s dive in so I can get back to work before I go to bed! Testing, Testing, 1, 2, 3 For the last couple hours, I’ve been writing unit tests for the ROM module. I’ve been going on and off with unit tests for a while. Before, I wanted to write unit test for cpu instructions before realising Blarrg made test ROMs for that. Because of that, test ROMs are the primary mode of testing out Riggurly, especially since test ROM developers know much more about specifics, like the halt bug, than I do. However, trest ROMs don’t test things like saving and loading, which is something I CAN write tests for in a reasonable amount of time. A lot of the work I’ve done was just making one helper function and figuring out what I wanted to be covered by it and what I want the tests to do. I might also refactor some repeated stuff into another helper for more efficient code after I finish writing the save round trip unit tests. That’s it for now friends, and don’t forget to listen to Flavor Foley’s new album, Mirror Modulation, which dropped today? on all music streaming platforms
Failing Tests Hello everybody! We’re back with another devlog! Let’s dive right in so I can got to bed! All Tests Pass A good bit of test writing has happened since the last devlog. The ROM suite has gone from a measly 3 tests to a much nicer 16, though still incomplete. I wrote more tests to test combinations of roms and save sizes that are meant to work, as well as a bunch of those that also test RTC functionality for MBC3. I ended up putting ROM construction into a helper, as well as making a helper for most of the save round trip tests, probably saving a few hundred lines of code. Next tests are probably going to be broken saves, saves that don’t match, etc. etc. to see that ROMs are preserved and such. Algospeak Going back to the RTC, I remade the function that adds a number of seconds to the internal RTC. What I was doing before was calling a function that ticked by ticks with an amount of ticks equal to 1 second FOR EACH SECOND. That’s often hundreds of thousands or millions of function calls, with O(n) time complexity. I was pretty easily able to swap it for an O(1) modulus thingy. Switchie-Shwoo Going back to ROM preservation, I recently realised that it was loading in new timestamp data before checking save validity, which could end up being problematic. I just swapped the order of those two and the problem was solved, though, as I said before, this needs to be test-enforced. That’s it for now folks! See y’all next time!
Scooby-Doo Hello friends! I just got an email from Stardance saying I have 8+ unlogged hours, so let’s dive in? I Wish That I Could [Fail] I haven’t exactly done much of aything since my last devlog. I’ve written two unit tests, I guess, which do partially (mostly) address the situation with ensuring that ROM state isn’t changed by a bad save, such as with an RTC or loading in a bad save. I covered the case for two MBCs (MBC1 and MBC3) that the number of banks for the save is mismatched with what ROM was loaded (eg. 4 banks on a 1 bank ROM or MBC2 style ram on an MBC3 rom, though I will cover this case in the future). As for where the time came from, it may be that I stared at my TODO list too hard, as I have my TODOs for Riggurly in one emacs split and my TODOs for school in the other, but I have my school.org in a completely different location in my FS, in my org directory ~/org instead of my my Riggurly directory ~/projects/riggurly, but I have no clue. I didn’t even edit either file for more than a total of 5 minutes!!! Anyway, that’s all for now folks! Make sure to keep listening to Jamie Paige and drop @ElffawIThink a follow! Also, the novel is a stupid ad I saw on Royal Road whiul ereading 1% lifesteal...
|
1792572850.1101463
|
1793177650.1101463
|
|
30453
|
slate by @swedishsplidney | Stardance
|
swedishsplidney
|
0
|
150.35
|
0
|
[]
|
[{"post_id": 73395, "ts": "202 [{"post_id": 73395, "ts": "2026-09-25T01:57:08Z", "hours": 8.295, "text": "shadows this one is really cool, i feel like it really takes it from looking like its from 1995 to like… 2015 maybe? it doesnt look that good but i think its a massive improvement! right now, its a 4096x4096 shadowmap with an extent of 24 units, a random noise filter to reduce pixelation, and 3x3 pcf to make it look somewhat smooth it is currently orthographic light projection only, but I think i’ll work on getting perspective lighting soon :) shadows did reqire implementing a whole new imageview, descriptor set, render pass, all that, but i’m getting used to it atp lol serialization is also completely broken right now, so I do need to fix that…"}, {"post_id": 67098, "ts": "2026-09-13T04:51:35Z", "hours": 7.6267, "text": "texture loading!!! absolute yipee moment this took like a lot of thinking to figure out how to implement for reasons that i think are obvious, but some of them are: how tf do you load an image what does the loaded image data mean how do you map the texture to the model’s uv coords how does normal mapping work how does the shader know where on the image its reading and a lot more that i prolly forgot lol this whole thing basically meant a whole shader overhaul again (even though i alr did that last devlog but its whatever loll) you can now map albedo (color), normal (height/offset), orm (occlusion, roughness, metallic), and emmision (glowiness) emission only kind of works rn lol, but i will get to it soon also, you currently have to manually set filepaths to the textures you want in the .json sidecar for that model, i need to add filepicker stuff to the inspector but i will get to it ok the textures in the video below were manually baked, packed, etc at 4096*4096 in blender i think another feature i should work on soon is procedural material generating, because texture mapping will always have a resolution limit (even 4k textures look kinda bad when mapped to larger models) i also want to change how reflections work a little bit, or maybe add anti-aliasing because right now the reflections look deep fried (as you will see in the video below)"}, {"post_id": 65721, "ts": "2026-09-11T02:04:30Z", "hours": 7.1881, "text": "shader overhaul so i noticed that the pbr shader lowkey looked kinda ugly and my microfacet solver was buns af so i js spent a while fixing it up and basically ended up going down a rabbit hole of tonemapping and completely rewrote the whole thing i switched from some random generic tonemapping to khronos standard neutral pbr mapping, which i think looks wayyyyyy better, the colors actually feel accurate now i also spent a little while setting up the shader to support texture colors when i get that all working it is wayyy better now, especially metallic things i also started setting up for texture imports but right now its only albedo and it does NOT work in the slightest lmao i think i will hopefully have texture importing and mapping done by next devlog, but uhhhhh…. no promises"}, {"post_id": 63897, "ts": "2026-09-07T22:41:08Z", "hours": 8.7083, "text": "physics aaaaaaaaaaaaaaaaa it works concerningly well and it actually makes me really happy to see it working i legit jumped for joy when i first saw stuff dropping due to gravity the actual underlying physics engine is jolt physics, but all the init and handling and all that is custom from me its all linked to the inspector and allat, so you can tweak values real time, such as body type, bounciness, friction, and mass its now a physics playground instead of just a glorified obj viewer scaling is very broken as far as colliders go, ill probably fix that soon you can start and stop the physics sim with f5"}, {"post_id": 62615, "ts": "2026-09-06T02:39:32Z", "hours": 5.9828, "text": "working scene serialization (saving) if you hit ctrl + s (or hit the gui button) then it creates a .slate file, containing all the data of every object in the scene this .slate file is human readable (and editable), written in a format very similar to toml heres an example .slate file: [ scene ] name = \"SlateScene\" version = \"1.0\" [[ entity ]] index = 0 name = \"\" mesh_path = \"models/test_obj.obj\" position = [ -0.310723 , 0.927019 , 1.47743 ] rotation = [ 0 , -0 , 0 ] scale = [ 1.9916 , 2.28848 , 3.38984 ] [ entity . material ] albedo = [ 0.183333 , 1 , 0 , 1 ] roughness = 0.5 metallic = 1 ior = 1.5 transmission = 0 [[ entity ]] index = 1 name = \"\" mesh_path = \"models/emerald_monkey.obj\" position = [ 1.29034 , 0 , -5.64025e-08 ] rotation = [ 8.61461 , 6.18611 , 2.71029 ] scale = [ 3.07852 , 3.38535 , 3.86504 ] [ entity . material ] albedo = [ 0.050432 , 0.800023 , 0.302281 , 1 ] roughness = 0 metallic = 0 ior = 2.418 transmission = 1 next, ill probably start working on physics"}, {"post_id": 61285, "ts": "2026-09-04T04:09:23Z", "hours": 8.2292, "text": "scene serialization okay so this is akward but it doesnt actually work yet, but in my defense serialization is really confusing to me since i generally don’t do a whole lot with file writing and loading, especially in a custom formatted and extensioned .slate file it basically just takes a bunch of data by looping through scene objects and records them, then it also has to do that backwards, then i also have to implement a command and gui system to trigger the logic and i also want the .slate to be easily readable, so i need to format the text in a way that makes sense (i’m taking inspiration from TOML) hopefully its done next devlog, but no promises lol"}, {"post_id": 59368, "ts": "2026-09-01T04:03:06Z", "hours": 8.3792, "text": "color picking! i noticed that setting color in the inspector with 3 rgb values was really annoying, so i made a whole new ui class ( ui_color_picker.hpp/cpp ), that can hook into any other ui it supports: S/B box + hue bar RGB input CMYK input (mildly broken) hex input (very broken) it actually works pretty well, there is some bug where an object can never be fully white but i think thats a command issue not color picker or inspector next, it might be time to work on physics"}, {"post_id": 58306, "ts": "2026-08-30T17:59:13Z", "hours": 8.5094, "text": "bugfixes and writing docs i spent a good 4 hours just going through some code, finding anything that might be wrong and fixing them. surprisingly, i didnt find that much, which means that i’m better at coding than i thought i found a few places where i just forgot to add something to a cleanup function, which could have led to a small memory leak over time, but nothing major some bugs i fixed: the background of any dropdown menu was rendering behind the viewport selecting stuff in the hierarchy wouldnt sync with the inspector properly i forgot to add screenPointToRay to my cleanup function some other things that i forgot i also added a few std::couts in some spots that needed them i also decided to start writing some docs because my brain was fried i basically just dug through my ~3000 lines of vulkan init and rendering code, and took notes throughout and tried explaining my confusing mental processes in a .md file this kinda made me realize that i do not have enough annotation in my code but thats a problem for future me :)"}, {"post_id": 56986, "ts": "2026-08-29T01:24:41Z", "hours": 8.5281, "text": "hierarchy panel and scrolling the hierarchy panel itself was actually not that hard, the hard part was coming up with a reliable system that can detect and list every object currently loaded in the scene without turning your pc into an explosive the other hard part was modifying the dockspace layout to be able to handle multiple different panels, because previously, it was just one window per dockspace panel, but now it can be as many as you want also, the scrolling system is new, it is its own class ui_scrollable_container , and it is basically doing what the dockspace used to do, but it also lets you scroll. so now, the dockspace just loads a scrollable container, and the scrollable container does all the arrangement and sizing logic. the hierarchy works exactly as you would expect it to, it lists current scene objects and then you can select them from it eventually, ill prolly add support for parenting, which should be pretty easy its kinda crazy to see my custom persistent ui system slowly turn into something useful as i add more features to it so far ive just been adding features when i need them, i havent just thought of features i might need and added all of them"}, {"post_id": 55367, "ts": "2026-08-27T04:34:36Z", "hours": 8.7392, "text": "inspector updates and dropdowns the inspector now has IOR and transmission values that you can change, ui updates, and dropdowns this took literally like 45 epochs because i was running into a materialgpu indexing issue where the inspector boxes would update the wrong values and my peanut brain didnt even think to check if i set the indices correctly dropdowns also took a good 3-4 hours, but it wasnt too bad, mostly just figuring out the correct logic in total, the dropdown class (ui_dropdown.cpp/hpp) is at 169 lines, and the inspector class (ui_inspector_panel.cpp/hpp) is at 470 lines next, i think ill either work on ui rounding or a file hierarchy viewer"}, {"post_id": 52171, "ts": "2026-08-22T08:13:19Z", "hours": 8.115, "text": "ui inspector the inspector is by no means done, but its a start! right now its just supposed to be purely functional, but i plan on making it look better in the future the input boxes are their own ui class, meaning i can have those input boxes everywhere making the ui system from scratch is very fun, but it at times i do regret not using qml or im gui, but overall, i dont regret making it from scratch too much the main issues with it right now is that the sensitivity values in the input boxes feel wrong, i think the position needs less sensitivity and the rotation needs more also, the color ones are a bit broken, theres a little bit of discrepancy between what the ui shows and what the actual .json material sidecar has written, so i’ll have to figure out how to fix that i think at some point, i want to add support for dropdowns, so each row can be expanded into larger input boxes if needed, or so the entire transform or material section can be collapsed i also need to add input boxes for IOR and transmission, i jus forgot to do that lol, should be pretty easy tho"}, {"post_id": 49355, "ts": "2026-08-18T04:12:43Z", "hours": 5.6403, "text": "rotation gizmo, multi-object support you can now switch between the translation gizmo, and the rotation gizmo, and click between objects to change what one has the gizmo active the gizmo also stays locked to the selected meshes’ local axes, i might add switching between local and global axis orientation at some point, but for right now local only is good enough all of the gizmo mesh is generated from code, not as an obj, making it very efficient and fast and robust, but making the logic that generates vertices in the right position was very annoying lol next, ill probably work on an inspector panel where you can edit an object’s location, rotation, scale, material data, etc or maybe saving the scene to file but now you can move and rotate any amount of objects to your heart’s content"}, {"post_id": 47420, "ts": "2026-08-15T10:46:06Z", "hours": 9.2511, "text": "translation gizmo this was like miserable to make lol it took me sooooooo long to get translation working, basically i had this samurai battle with a scaling issue for the longest time, i accidentally told my code that the viewport was smaller than it actually was and it just took forever to find that out this issue caused a really annoying issue where the actual location of the rendered gizmo was different than the clickable area there was also this other issue where the gizmo would move twice the speed of the mesh it was translating, but it was just a flaw with the mesh class, the gizmo was basically constantly recieving its own center as well as the meshes’ center which made it translate double but it works now, i think it works pretty well its all based off of the modular command system, so anything can call translatemeshcommand and it translate meshes next is prolly working on rotate and scale and stuff, all the other gizmos that we need the gizmos also meant a new .vert and .frag pair, and a whole new vulkan render pipeline lol"}, {"post_id": 43684, "ts": "2026-08-09T02:47:19Z", "hours": 9.0197, "text": "grid generation this took forever, and was much harder than i expected basically, i needed to create a whole new rendering pipeline and a whole new glsl shader pair, as well as coming up with logic so the triangle-focused renderer can actually render some super thin infinite lines the grid does generate dynamically around the camera view, so it is endless i think it does look pretty good tho next, ill prolly work on adding some move and rotation and maybe even scale tools"}, {"post_id": 37279, "ts": "2026-07-29T21:25:56Z", "hours": 9.8689, "text": "mesh importing and refraction ima start off by saying that this isn’t true refraction, since that requires path tracing, which is really computationally intensive instead, i went with screen space refraction offset approximation , which is a good, efficient refraction approximator that i still think looks pretty cool material data for refraction the value that determines the transparency of an object is called transmission , and the value for refraction is index of refraction (or IOR) the current filesystem for materials that i have is .mtl files, because thats the standard filetype for exporting materials with objs from programs like blender. however, .mtl is a filesystem from the 1990s that was originally created for phong systems, meaning it doesnt really contain transmission values so, i created a json sidecar system, that generates a better, more robust, human readable material file for every model. so now, when you import an obj/mtl pair: it checks for a json sidecar file if there is one already, just load from that and finish! if there isn’t one already, now read the .mtl, extract as much info as you can from that, and write it to a new json file multi pass rendering in order to correctly render transparent meshes, you need at least 2 render passes (render the opaque ones first, then the transparent ones) also, for the refraction to work, it needs an image of everything behind it, so in between the opaque and transparent pass, it quickly copies everything on the screen to an offscreen framebuffer how the refraction works 1. get the view vector (v): this one’s simple, just evaluate the view direction from the fragment position to the camera position also, adjust the normal (n) if rendering backface 2. refraction vector (snell’s law): the refraction vector is actually easy too, glsl has a built in refract intrinsic so you just drive that with a clamped ior value 3. deviation mapping: to determine how much to bend the background scene texture, just calculate the deviation vector between the refracted ray and the view ray: vec3 rayDeviation = refractDir - (- V ); vec2 distortion = ( push . viewProjMatrix * vec4 ( rayDeviation , 0.0 )). xy * 0.05 * transmission ; 4. uv sampling + composite blending offset the screen coordinates, but in order to prevent the edges from exploding, it needs to be clamped also, sample the background buffer to blend lighting and make a fresnel rim other stuff i did: i also made a full custom persistent ui system (this took a while), and a command system, because hardcoding the ui to do stuff is bad so instead of the ui code directly running logic, clicking a button just sends a command to the command system this meant creating another render pass, meaning i’m up to 3 render passes per frame now the only command so far is file.import_mesh , which is also hooked into the ui (you can see in the video below)"}, {"post_id": 35648, "ts": "2026-07-27T07:01:43Z", "hours": 7.2731, "text": "pbr materials pbr math reflectance: this is the main equation that a cook-torrance pbr system, all the other equations lead back to this one at some point 𝐿𝑜(𝑝,𝜔𝑜) =𝐿𝑒(𝑝,𝜔𝑜) +∫Ω𝑓𝑟(𝑝,𝜔𝑖,𝜔𝑜)𝐿𝑖(𝑝,𝜔𝑖)(𝐧⋅𝜔𝑖)𝑑𝜔𝑖 this equation essentially just determines what light does after hitting a surface with specific parameters we approximate the integral with two different light interactions: diffuse (𝑘𝐷): any light that penetrates the surface, scatters internally, and re-emerges specular (𝑘𝑆): light that reflects directly off the micro-surface in the code itself, the total outgoing radiance ($L_o$) is calculated with: vec3 Lo = ( kD * albedo / PI + specular ) * radiance * NdotL ; cook-torrance brdf: the specular component is calculated using the cook-torrance microfacet brdf: ## 𝑓cook-torrance =𝐷⋅𝐺⋅𝐹 / 4(𝐍⋅𝐕)(𝐍⋅𝐋) where 𝐷, 𝐺, and 𝐹 represent: normal distribution (𝐷): the NDF statistical distribution calculates how many microfacets are aligned with the halfway vector ($𝐇=𝐕+𝐋 / abs(𝐕+𝐋)) 𝐷GGX(𝐍,𝐇,𝛼) = 𝛼^2 / 𝜋((𝐍⋅𝐇)2(𝛼2−1)+1)2 ```glsl float DistributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness * roughness; float a2 = a * a; float NdotH = max(dot(N, H), 0.0); float NdotH2 = NdotH * NdotH; float num = a2; float denom = (NdotH2 * (a2 - 1.0) + 1.0); denom = PI * denom * denom; return num / max(denom, 0.000001); } ``` geometry (𝐺): smiths schlick-ggx microfacets can cast shadows and obscure light from the camera too, so the geometry function calculates the self shading factor using smiths method: 𝐺(𝐍,𝐕,𝐋,𝑘) =𝐺1(𝐍,𝐕,𝑘) ⋅𝐺1(𝐍,𝐋,𝑘) where 𝑘 maps roughness for direct lighting using 𝑘 = (roughness+1)^2 / 8: ```glsl float GeometrySchlickGGX(float NdotV, float roughness) { float r = (roughness + 1.0); float k = (r * r) / 8.0; float num = NdotV; float denom = NdotV * (1.0 - k) + k; return num / denom; } float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; } ``` fresnel equation (𝐹): fresnel-schlick approximation the fresnel factor calculates the percentage of light reflected versus absorbed based on the viewing angle (𝜃): 𝐹(𝜃,𝐹0) =𝐹0 +(1−𝐹0)(1−cos𝜃)^5 nonmetals reflect around 4% of light (𝐹0 =0.04), while metals (conductors) use their tinted base albedo color as 𝐹0: ```glsl vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); } // lower, inside main(): vec3 F0 = mix(vec3(0.04), albedo, metallic); vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); ``` other stuff: a surface cant reflect more light than it recieves, so a simple equation is used to make sure this doesnt happen: ## 𝐤𝐃 =(1−𝐤𝐒) ⋅(1−metallic) right now, all the material data is pulled from a .mtl file i also added bindless ssbo storage so it doesnt explode when you have a lot of models next, i might keep working on the custom persistent ui system"}, {"post_id": 32732, "ts": "2026-07-22T02:20:30Z", "hours": 8.3064, "text": "custom obj file loading all of the obj loading is done using tinyobjloader , which is a popular header file that im using to get the raw vertex coords of any .obj file however, all tinyobj does is pass the raw vertex data to my code (technically it can do some basic triangulation, but thats not its main purpose so its not perfect) this means that any model with n-gons (complex polygons) will not work and will look super weird technically you can tell blender to explicitly triangulate a file when you export it, but i think that it should be able to take any obj that you can throw at it without needing external setup so i also added a popular header file called earcut , which at a basic level, just cuts any complex polygons into 3-vertex triangles (which my index pass and renderer accepts). this now means that it can load any custom obj file i want to add auto color loading (from the obj data) or texture loading next, or maybe work on the ui or add ambient occlusion"}, {"post_id": 25108, "ts": "2026-07-09T03:12:17Z", "hours": 6.5614, "text": "entered the 3rd dimension wasd movement made a whole separate Camera class (look at me keepin things organized ) this means that now the vulkan_renderer class has model, projection, view, and transform matrices: the model matrix tracks the transformations the view matrix essentially tracks where the camera is the projection matrix sets a perspective frustum in order to make things actually have perspective and not just be flat the lerping is also based on deltaTime, which means smooth movement no matter ur fps the mouse tracking is locked via SDL_SetWindowRelativeMouseMode , and can be unlocked with the escape key vertices and index buffers right now, the vertices are being set manually in my code… however, importing obj (and maybe gltf and stl) files is next on the feature list, i’m planning on using tinyobj all the geometry is indexed, which basically means that instead of wasting a ton of memory repeating data for 36 raw vertices, you can just define the 8 corner vertices, then use an index buffer pass to tell the gpu how to connect those 8 vertices into 12 triangles window resizing this was easy, basically just had to tell it to make new image views and framebuffers based on the current aspect ratio (w/h) when the size of the window changes so now, its a spinning cube in a resizeable window that you can move around with wasd"}, {"post_id": 22507, "ts": "2026-07-05T00:42:28Z", "hours": 6.1236, "text": "the window opens (!!) so, why did it take me 6 hours to get a window to open? it kinda just comes down to the fact that vulkan is vulkan on other apis such as opengl, the graphics driver just does stuff for you. you can say sumn like “open a window and make it pink” and it just does the memory, thread syncing, and goofy platform stuff for you vulkan on the other hand, wants none of that. if you dont specifically ask for something, it wont happen. this does make it significantly harder (and much easier to create a gpu-ending vram overflow) to code with, but it does have the benifit of being much more powerful, having no overhead, and 100% control. its not gonna do anything that you dont tell it to so what do you have to do to open a beautiful grey window with vulkan? os bridge (sdl3): instead of writing a whole abstraction for wayland/x11, win32 and metal, you just have to tell sdl3 that you’re making a vulkan window, and it does some magic stuff vulkan instance ( VkInstance ): this is how you get into the vulkan sdk. you kinda just have to tell vulkan everything it needs to know and ask sdl what extensions you need window surface ( VkSurfaceKHR ): vulkan is kinda dumb and doesnt know what a window actually is, so this is where sdl tells vulkan where it can actually write pixels pick a physical device: scan the current hardware, loop through every available device, and then just pick the best one (eventually ill probably add a way to select a custom one) logical device ( VkDevice ): the physical device is the actual doohickey in your computer that runs stuff, the logical device is the software’s interface to it. swapchain ( VkSwapchainKHR ): vulkan can’t actually write images directly to the screen (and that would look bad anyway), so you make a swapchain. my code does double buffering (basically vsync), which means that the screen displays image a, while the gpu is rendering image b. render pass and framebuffers: basically just telling vulkan what to do (in this case, clear the screen, then draw the color). the framebuffer just binds the swapchain image views to the render pass synchronization and command subsystem: believe it or not, your gpu’s clock speed is usually around (or more than) half of your cpu’s clock speed, so you have to make sure that your code waits for the gpu is ready before running the next c++ line. if you don’t, your gpu ram and cache will overflow. overall, this took a total of 764 lines of code just to get a grey window the nice part is that the progress that i’ll make is exponential, since the very beginning stuff takes the longest i also made a logo (the current project page banner)"}]...
|
Title: slate by @swedishsplidney | Stardance
Devl Title: slate by @swedishsplidney | Stardance
Devlogs:
the window opens (!!) so, why did it take me 6 hours to get a window to open? it kinda just comes down to the fact that vulkan is vulkan on other apis such as opengl, the graphics driver just does stuff for you. you can say sumn like “open a window and make it pink” and it just does the memory, thread syncing, and goofy platform stuff for you vulkan on the other hand, wants none of that. if you dont specifically ask for something, it wont happen. this does make it significantly harder (and much easier to create a gpu-ending vram overflow) to code with, but it does have the benifit of being much more powerful, having no overhead, and 100% control. its not gonna do anything that you dont tell it to so what do you have to do to open a beautiful grey window with vulkan? os bridge (sdl3): instead of writing a whole abstraction for wayland/x11, win32 and metal, you just have to tell sdl3 that you’re making a vulkan window, and it does some magic stuff vulkan instance ( VkInstance ): this is how you get into the vulkan sdk. you kinda just have to tell vulkan everything it needs to know and ask sdl what extensions you need window surface ( VkSurfaceKHR ): vulkan is kinda dumb and doesnt know what a window actually is, so this is where sdl tells vulkan where it can actually write pixels pick a physical device: scan the current hardware, loop through every available device, and then just pick the best one (eventually ill probably add a way to select a custom one) logical device ( VkDevice ): the physical device is the actual doohickey in your computer that runs stuff, the logical device is the software’s interface to it. swapchain ( VkSwapchainKHR ): vulkan can’t actually write images directly to the screen (and that would look bad anyway), so you make a swapchain. my code does double buffering (basically vsync), which means that the screen displays image a, while the gpu is rendering image b. render pass and framebuffers: basically just telling vulkan what to do (in this case, clear the screen, then draw the color). the framebuffer just binds the swapchain image views to the render pass synchronization and command subsystem: believe it or not, your gpu’s clock speed is usually around (or more than) half of your cpu’s clock speed, so you have to make sure that your code waits for the gpu is ready before running the next c++ line. if you don’t, your gpu ram and cache will overflow. overall, this took a total of 764 lines of code just to get a grey window the nice part is that the progress that i’ll make is exponential, since the very beginning stuff takes the longest i also made a logo (the current project page banner)
entered the 3rd dimension wasd movement made a whole separate Camera class (look at me keepin things organized ) this means that now the vulkan_renderer class has model, projection, view, and transform matrices: the model matrix tracks the transformations the view matrix essentially tracks where the camera is the projection matrix sets a perspective frustum in order to make things actually have perspective and not just be flat the lerping is also based on deltaTime, which means smooth movement no matter ur fps the mouse tracking is locked via SDL_SetWindowRelativeMouseMode , and can be unlocked with the escape key vertices and index buffers right now, the vertices are being set manually in my code… however, importing obj (and maybe gltf and stl) files is next on the feature list, i’m planning on using tinyobj all the geometry is indexed, which basically means that instead of wasting a ton of memory repeating data for 36 raw vertices, you can just define the 8 corner vertices, then use an index buffer pass to tell the gpu how to connect those 8 vertices into 12 triangles window resizing this was easy, basically just had to tell it to make new image views and framebuffers based on the current aspect ratio (w/h) when the size of the window changes so now, its a spinning cube in a resizeable window that you can move around with wasd
custom obj file loading all of the obj loading is done using tinyobjloader , which is a popular header file that im using to get the raw vertex coords of any .obj file however, all tinyobj does is pass the raw vertex data to my code (technically it can do some basic triangulation, but thats not its main purpose so its not perfect) this means that any model with n-gons (complex polygons) will not work and will look super weird technically you can tell blender to explicitly triangulate a file when you export it, but i think that it should be able to take any obj that you can throw at it without needing external setup so i also added a popular header file called earcut , which at a basic level, just cuts any complex polygons into 3-vertex triangles (which my index pass and renderer accepts). this now means that it can load any custom obj file i want to add auto color loading (from the obj data) or texture loading next, or maybe work on the ui or add ambient occlusion
pbr materials pbr math reflectance: this is the main equation that a cook-torrance pbr system, all the other equations lead back to this one at some point 𝐿𝑜(𝑝,𝜔𝑜) =𝐿𝑒(𝑝,𝜔𝑜) +∫Ω𝑓𝑟(𝑝,𝜔𝑖,𝜔𝑜)𝐿𝑖(𝑝,𝜔𝑖)(𝐧⋅𝜔𝑖)𝑑𝜔𝑖 this equation essentially just determines what light does after hitting a surface with specific parameters we approximate the integral with two different light interactions: diffuse (𝑘𝐷): any light that penetrates the surface, scatters internally, and re-emerges specular (𝑘𝑆): light that reflects directly off the micro-surface in the code itself, the total outgoing radiance ($L_o$) is calculated with: vec3 Lo = ( kD * albedo / PI + specular ) * radiance * NdotL ; cook-torrance brdf: the specular component is calculated using the cook-torrance microfacet brdf: ## 𝑓cook-torrance =𝐷⋅𝐺⋅𝐹 / 4(𝐍⋅𝐕)(𝐍⋅𝐋) where 𝐷, 𝐺, and 𝐹 represent: normal distribution (𝐷): the NDF statistical distribution calculates how many microfacets are aligned with the halfway vector ($𝐇=𝐕+𝐋 / abs(𝐕+𝐋)) 𝐷GGX(𝐍,𝐇,𝛼) = 𝛼^2 / 𝜋((𝐍⋅𝐇)2(𝛼2−1)+1)2 ```glsl float DistributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness * roughness; float a2 = a * a; float NdotH = max(dot(N, H), 0.0); float NdotH2 = NdotH * NdotH; float num = a2; float denom = (NdotH2 * (a2 - 1.0) + 1.0); denom = PI * denom * denom; return num / max(denom, 0.000001); } ``` geometry (𝐺): smiths schlick-ggx microfacets can cast shadows and obscure light from the camera too, so the geometry function calculates the self shading factor using smiths method: 𝐺(𝐍,𝐕,𝐋,𝑘) =𝐺1(𝐍,𝐕,𝑘) ⋅𝐺1(𝐍,𝐋,𝑘) where 𝑘 maps roughness for direct lighting using 𝑘 = (roughness+1)^2 / 8: ```glsl float GeometrySchlickGGX(float NdotV, float roughness) { float r = (roughness + 1.0); float k = (r * r) / 8.0; float num = NdotV; float denom = NdotV * (1.0 - k) + k; return num / denom; } float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; } ``` fresnel equation (𝐹): fresnel-schlick approximation the fresnel factor calculates the percentage of light reflected versus absorbed based on the viewing angle (𝜃): 𝐹(𝜃,𝐹0) =𝐹0 +(1−𝐹0)(1−cos𝜃)^5 nonmetals reflect around 4% of light (𝐹0 =0.04), while metals (conductors) use their tinted base albedo color as 𝐹0: ```glsl vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); } // lower, inside main(): vec3 F0 = mix(vec3(0.04), albedo, metallic); vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); ``` other stuff: a surface cant reflect more light than it recieves, so a simple equation is used to make sure this doesnt happen: ## 𝐤𝐃 =(1−𝐤𝐒) ⋅(1−metallic) right now, all the material data is pulled from a .mtl file i also added bindless ssbo storage so it doesnt explode when you have a lot of models next, i might keep working on the custom persistent ui system
mesh importing and refraction ima start off by saying that this isn’t true refraction, since that requires path tracing, which is really computationally intensive instead, i went with screen space refraction offset approximation , which is a good, efficient refraction approximator that i still think looks pretty cool material data for refraction the value that determines the transparency of an object is called transmission , and the value for refraction is index of refraction (or IOR) the current filesystem for materials that i have is .mtl files, because thats the standard filetype for exporting materials with objs from programs like blender. however, .mtl is a filesystem from the 1990s that was originally created for phong systems, meaning it doesnt really contain transmission values so, i created a json sidecar system, that generates a better, more robust, human readable material file for every model. so now, when you import an obj/mtl pair: it checks for a json sidecar file if there is one already, just load from that and finish! if there isn’t one already, now read the .mtl, extract as much info as you can from that, and write it to a new json file multi pass rendering in order to correctly render transparent meshes, you need at least 2 render passes (render the opaque ones first, then the transparent ones) also, for the refraction to work, it needs an image of everything behind it, so in between the opaque and transparent pass, it quickly copies everything on the screen to an offscreen framebuffer how the refraction works 1. get the view vector (v): this one’s simple, just evaluate the view direction from the fragment position to the camera position also, adjust the normal (n) if rendering backface 2. refraction vector (snell’s law): the refraction vector is actually easy too, glsl has a built in refract intrinsic so you just drive that with a clamped ior value 3. deviation mapping: to determine how much to bend the background scene texture, just calculate the deviation vector between the refracted ray and the view ray: vec3 rayDeviation = refractDir - (- V ); vec2 distortion = ( push . viewProjMatrix * vec4 ( rayDeviation , 0.0 )). xy * 0.05 * transmission ; 4. uv sampling + composite blending offset the screen coordinates, but in order to prevent the edges from exploding, it needs to be clamped also, sample the background buffer to blend lighting and make a fresnel rim other stuff i did: i also made a full custom persistent ui system (this took a while), and a command system, because hardcoding the ui to do stuff is bad so instead of the ui code directly running logic, clicking a button just sends a command to the command system this meant creating another render pass, meaning i’m up to 3 render passes per frame now the only command so far is file.import_mesh , which is also hooked into the ui (you can see in the video below)
grid generation this took forever, and was much harder than i expected basically, i needed to create a whole new rendering pipeline and a whole new glsl shader pair, as well as coming up with logic so the triangle-focused renderer can actually render some super thin infinite lines the grid does generate dynamically around the camera view, so it is endless i think it does look pretty good tho next, ill prolly work on adding some move and rotation and maybe even scale tools
translation gizmo this was like miserable to make lol it took me sooooooo long to get translation working, basically i had this samurai battle with a scaling issue for the longest time, i accidentally told my code that the viewport was smaller than it actually was and it just took forever to find that out this issue caused a really annoying issue where the actual location of the rendered gizmo was different than the clickable area there was also this other issue where the gizmo would move twice the speed of the mesh it was translating, but it was just a flaw with the mesh class, the gizmo was basically constantly recieving its own center as well as the meshes’ center which made it translate double but it works now, i think it works pretty well its all based off of the modular command system, so anything can call translatemeshcommand and it translate meshes next is prolly working on rotate and scale and stuff, all the other gizmos that we need the gizmos also meant a new .vert and .frag pair, and a whole new vulkan render pipeline lol
rotation gizmo, multi-object support you can now switch between the translation gizmo, and the rotation gizmo, and click between objects to change what one has the gizmo active the gizmo also stays locked to the selected meshes’ local axes, i might add switching between local and global axis orientation at some point, but for right now local only is good enough all of the gizmo mesh is generated from code, not as an obj, making it very efficient and fast and robust, but making the logic that generates vertices in the right position was very annoying lol next, ill probably work on an inspector panel where you can edit an object’s location, rotation, scale, material data, etc or maybe saving the scene to file but now you can move and rotate any amount of objects to your heart’s content
ui inspector the inspector is by no means done, but its a start! right now its just supposed to be purely functional, but i plan on making it look better in the future the input boxes are their own ui class, meaning i can have those input boxes everywhere making the ui system from scratch is very fun, but it at times i do regret not using qml or im gui, but overall, i dont regret making it from scratch too much the main issues with it right now is that the sensitivity values in the input boxes feel wrong, i think the position needs less sensitivity and the rotation needs more also, the color ones are a bit broken, theres a little bit of discrepancy between what the ui shows and what the actual .json material sidecar has written, so i’ll have to figure out how to fix that i think at some point, i want to add support for dropdowns, so each row can be expanded into larger input boxes if needed, or so the entire transform or material section can be collapsed i also need to add input boxes for IOR and transmission, i jus forgot to do that lol, should be pretty easy tho
inspector updates and dropdowns the inspector now has IOR and transmission values that you can change, ui updates, and dropdowns this took literally like 45 epochs because i was running into a materialgpu indexing issue where the inspector boxes would update the wrong values and my peanut brain didnt even think to check if i set the indices correctly dropdowns also took a good 3-4 hours, but it wasnt too bad, mostly just figuring out the correct logic in total, the dropdown class (ui_dropdown.cpp/hpp) is at 169 lines, and the inspector class (ui_inspector_panel.cpp/hpp) is at 470 lines next, i think ill either work on ui rounding or a file hierarchy viewer
hierarchy panel and scrolling the hierarchy panel itself was actually not that hard, the hard part was coming up with a reliable system that can detect and list every object currently loaded in the scene without turning your pc into an explosive the other hard part was modifying the dockspace layout to be able to handle multiple different panels, because previously, it was just one window per dockspace panel, but now it can be as many as you want also, the scrolling system is new, it is its own class ui_scrollable_container , and it is basically doing what the dockspace used to do, but it also lets you scroll. so now, the dockspace just loads a scrollable container, and the scrollable container does all the arrangement and sizing logic. the hierarchy works exactly as you would expect it to, it lists current scene objects and then you can select them from it eventually, ill prolly add support for parenting, which should be pretty easy its kinda crazy to see my custom persistent ui system slowly turn into something useful as i add more features to it so far ive just been adding features when i need them, i havent just thought of features i might need and added all of them
bugfixes and writing docs i spent a good 4 hours just going through some code, finding anything that might be wrong and fixing them. surprisingly, i didnt find that much, which means that i’m better at coding than i thought i found a few places where i just forgot to add something to a cleanup function, which could have led to a small memory leak over time, but nothing major some bugs i fixed: the background of any dropdown menu was rendering behind the viewport selecting stuff in the hierarchy wouldnt sync with the inspector properly i forgot to add screenPointToRay to my cleanup function some other things that i forgot i also added a few std::couts in some spots that needed them i also decided to start writing some docs because my brain was fried i basically just dug through my ~3000 lines of vulkan init and rendering code, and took notes throughout and tried explaining my confusing mental processes in a .md file this kinda made me realize that i do not have enough annotation in my code but thats a problem for future me :)
color picking! i noticed that setting color in the inspector with 3 rgb values was really annoying, so i made a whole new ui class ( ui_color_picker.hpp/cpp ), that can hook into any other ui it supports: S/B box + hue bar RGB input CMYK input (mildly broken) hex input (very broken) it actually works pretty well, there is some bug where an object can never be fully white but i think thats a command issue not color picker or inspector next, it might be time to work on physics
scene serialization okay so this is akward but it doesnt actually work yet, but in my defense serialization is really confusing to me since i generally don’t do a whole lot with file writing and loading, especially in a custom formatted and extensioned .slate file it basically just takes a bunch of data by looping through scene objects and records them, then it also has to do that backwards, then i also have to implement a command and gui system to trigger the logic and i also want the .slate to be easily readable, so i need to format the text in a way that makes sense (i’m taking inspiration from TOML) hopefully its done next devlog, but no promises lol
working scene serialization (saving) if you hit ctrl + s (or hit the gui button) then it creates a .slate file, containing all the data of every object in the scene this .slate file is human readable (and editable), written in a format very similar to toml heres an example .slate file: [ scene ] name = "SlateScene" version = "1.0" [[ entity ]] index = 0 name = "" mesh_path = "models/test_obj.obj" position = [ -0.310723 , 0.927019 , 1.47743 ] rotation = [ 0 , -0 , 0 ] scale = [ 1.9916 , 2.28848 , 3.38984 ] [ entity . material ] albedo = [ 0.183333 , 1 , 0 , 1 ] roughness = 0.5 metallic = 1 ior = 1.5 transmission = 0 [[ entity ]] index = 1 name = "" mesh_path = "models/emerald_monkey.obj" position = [ 1.29034 , 0 , -5.64025e-08 ] rotation = [ 8.61461 , 6.18611 , 2.71029 ] scale = [ 3.07852 , 3.38535 , 3.86504 ] [ entity . material ] albedo = [ 0.050432 , 0.800023 , 0.302281 , 1 ] roughness = 0 metallic = 0 ior = 2.418 transmission = 1 next, ill probably start working on physics
physics aaaaaaaaaaaaaaaaa it works concerningly well and it actually makes me really happy to see it working i legit jumped for joy when i first saw stuff dropping due to gravity the actual underlying physics engine is jolt physics, but all the init and handling and all that is custom from me its all linked to the inspector and allat, so you can tweak values real time, such as body type, bounciness, friction, and mass its now a physics playground instead of just a glorified obj viewer scaling is very broken as far as colliders go, ill probably fix that soon you can start and stop the physics sim with f5
shader overhaul so i noticed that the pbr shader lowkey looked kinda ugly and my microfacet solver was buns af so i js spent a while fixing it up and basically ended up going down a rabbit hole of tonemapping and completely rewrote the whole thing i switched from some random generic tonemapping to khronos standard neutral pbr mapping, which i think looks wayyyyyy better, the colors actually feel accurate now i also spent a little while setting up the shader to support texture colors when i get that all working it is wayyy better now, especially metallic things i also started setting up for texture imports but right now its only albedo and it does NOT work in the slightest lmao i think i will hopefully have texture importing and mapping done by next devlog, but uhhhhh…. no promises
texture loading!!! absolute yipee moment this took like a lot of thinking to figure out how to implement for reasons that i think are obvious, but some of them are: how tf do you load an image what does the loaded image data mean how do you map the texture to the model’s uv coords how does normal mapping work how does the shader know where on the image its reading and a lot more that i prolly forgot lol this whole thing basically meant a whole shader overhaul again (even though i alr did that last devlog but its whatever loll) you can now map albedo (color), normal (height/offset), orm (occlusion, roughness, metallic), and emmision (glowiness) emission only kind of works rn lol, but i will get to it soon also, you currently have to manually set filepaths to the textures you want in the .json sidecar for that model, i need to add filepicker stuff to the inspector but i will get to it ok the textures in the video below were manually baked, packed, etc at 4096*4096 in blender i think another feature i should work on soon is procedural material generating, because texture mapping will always have a resolution limit (even 4k textures look kinda bad when mapped to larger models) i also want to change how reflections work a little bit, or maybe add anti-aliasing because right now the reflections look deep fried (as you will see in the video below)
shadows this one is really cool, i feel like it really takes it from looking like its from 1995 to like… 2015 maybe? it doesnt look that good but i think its a massive improvement! right now, its a 4096x4096 shadowmap with an extent of 24 units, a random noise filter to reduce pixelation, and 3x3 pcf to make it look somewhat smooth it is currently orthographic light projection only, but I think i’ll work on getting perspective lighting soon :) shadows did reqire implementing a whole new imageview, descriptor set, render pass, all that, but i’m getting used to it atp lol serialization is also completely broken right now, so I do need to fix that…...
|
1792774523.8995285
|
1793379323.8995285
|
|
7084
|
ALL of the board games I like or Invented by @Nezr ALL of the board games I like or Invented by @Nezruceuil | Stardance...
|
Nezruceuil
|
1
|
151.0
|
1265
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-30T19:52:05Z", "hours": 151.0, "multiplier": 8.44, "payout": 1265, "blessing": "neutral", "text": "I coded a Game in which you can play all of my favorite board games and the games that I invented. I deepened my Godot coding knowledge by recreating complicated games like: Chess, Reversi, Go, Tafl and Shōgi. I really enjoyed doing this project and now you can enjoy it too! hey, if you write **Stardance** in the background texture you can get this amazing background ↓. ✨", "repo": "https://github.com/Nezruceuil/my-board-games", "demo": "https://nezruceuil.itch.io/cool-board-games"}]...
|
[{"post_id": 75686, "ts": "202 [{"post_id": 75686, "ts": "2026-09-30T19:11:44Z", "hours": 1.1744, "text": "Fixed Bugs and added new secrets panels."}, {"post_id": 71414, "ts": "2026-09-20T19:42:26Z", "hours": 9.54, "text": "Fixed bugs, improved my game, added recommendation, added a way to order the games however you want, added art for amazons game fire and added a Chess panel."}, {"post_id": 61656, "ts": "2026-09-04T17:57:46Z", "hours": 5.3467, "text": "Improve my board game, fixed bugs, added a texture for the walls, added a play time estimation for every games, added Amazons and added Gomoku."}, {"post_id": 58882, "ts": "2026-08-31T13:38:25Z", "hours": 1.7225, "text": "Fixed more bugs (tafl and text in wrong place.) and improved Castle War and added some art to it."}, {"post_id": 58076, "ts": "2026-08-30T12:49:31Z", "hours": 10.0364, "text": "Fixed Bugs (tafl,drawback chess,ect…), Added a Spring panel, Added Amazons and Added Castle War (my own strategy game)."}, {"post_id": 48101, "ts": "2026-08-16T12:03:28Z", "hours": 9.0517, "text": "fixed Bugs, added 2 new chess variants and Added 15 unique drawbacks to drawback chess!"}, {"post_id": 46333, "ts": "2026-08-13T14:07:53Z", "hours": 9.0744, "text": "Fixed bugs, added a Hazelnut fight ai (not entirely done) and added more drawbacks to drawback chess."}, {"post_id": 43342, "ts": "2026-08-08T15:20:23Z", "hours": 8.5814, "text": "Added better panel for the main page (added a Summer panel a Winter panel that you can get by being on the game at the right season. and a star dance panel that you can get by typing “Stardance”. (I drew all of the panels myself.)) , added a Save and Load function, change all of the text font to be better and added a script to see the panel (control/option space)."}, {"post_id": 40696, "ts": "2026-08-04T11:49:37Z", "hours": 1.2158, "text": "fixed bugs, improved design and added a delete drawings and arrows function win delete. (a lot of arrows + delete button = no more arrows)"}, {"post_id": 39671, "ts": "2026-08-02T20:01:01Z", "hours": 4.1125, "text": "added an arrow drawing ability and fixed bugs."}, {"post_id": 38699, "ts": "2026-08-01T06:56:05Z", "hours": 1.4872, "text": "added an explanation for every games and fixed bugs with the lines of action game."}, {"post_id": 38178, "ts": "2026-07-31T13:20:17Z", "hours": 1.7389, "text": "fixed a bug with chess and added lines of action!"}, {"post_id": 37562, "ts": "2026-07-30T11:46:29Z", "hours": 3.5211, "text": "fixed bugs and added Go!"}, {"post_id": 37224, "ts": "2026-07-29T19:30:53Z", "hours": 1.4664, "text": "finished making talf games (added the king suffocation mechanic) !"}, {"post_id": 36562, "ts": "2026-07-28T19:20:19Z", "hours": 3.0928, "text": "started added talf game( ≈ 85% done) still need to add the king capture feature"}, {"post_id": 35321, "ts": "2026-07-26T17:18:12Z", "hours": 1.8692, "text": "fixed bugs and added more chess variants"}, {"post_id": 35080, "ts": "2026-07-26T08:26:26Z", "hours": 4.0767, "text": "Fixed bugs and added Reversi!"}, {"post_id": 34274, "ts": "2026-07-24T20:51:45Z", "hours": 2.0453, "text": "added bigger tic tac toe and fixed bugs."}, {"post_id": 33662, "ts": "2026-07-23T19:17:40Z", "hours": 2.0328, "text": "added 4 player Chess, fixed bugs and added a boat for Chaturaji."}, {"post_id": 33528, "ts": "2026-07-23T15:39:08Z", "hours": 2.0022, "text": "added an explanation for every game."}, {"post_id": 33148, "ts": "2026-07-22T19:53:27Z", "hours": 4.4092, "text": "fixed bugs and added Chaturaji"}, {"post_id": 31461, "ts": "2026-07-19T20:45:54Z", "hours": 4.3989, "text": "fixed a bug with duck chess and added Shōgi."}, {"post_id": 30822, "ts": "2026-07-18T19:09:14Z", "hours": 6.975, "text": "added every variants( Atomic,Duck Chess,Fog Of War,Giveaway,King Of The Hill,Capture Anything,Torpedo,No Castling,Ghost Board,Sideways ) and added Connect 4"}, {"post_id": 30297, "ts": "2026-07-17T19:08:42Z", "hours": 7.4939, "text": "Started adding an AI for Hazelnut Fight, fixed bugs with castling in the chess games, added a page to play classic chess variants, and a page where you can see all the chess variants coming in the next devlog. I also added an autofill that checks for similarity to fill in variant names (for example: aTuMyk -> Atomic .)"}, {"post_id": 27888, "ts": "2026-07-13T20:03:40Z", "hours": 0.7567, "text": "added color pickers to the options menu."}, {"post_id": 27847, "ts": "2026-07-13T18:47:36Z", "hours": 1.3992, "text": "added a classic version to pieces fit!"}, {"post_id": 24673, "ts": "2026-07-08T12:50:10Z", "hours": 4.3453, "text": "Added ‘Pieces Fit,’ a game where everyone is given the same randomly generated custom piece and must place as many copies of it as possible without any of them attacking each other."}, {"post_id": 24151, "ts": "2026-07-07T15:08:03Z", "hours": 3.4447, "text": "fixed bugs with some drawbacks and added 5 more uniques drawbacks"}, {"post_id": 22245, "ts": "2026-07-04T16:41:55Z", "hours": 2.2403, "text": "added a resign button 5 more unique drawback and fixed bugs"}, {"post_id": 22065, "ts": "2026-07-04T09:47:55Z", "hours": 3.5369, "text": "added Drawback Chess with 15 drawbacks!"}, {"post_id": 20990, "ts": "2026-07-02T18:48:12Z", "hours": 3.8056, "text": "added draw to the chess games and a options menu (that doesn’t do anything yet)"}, {"post_id": 19446, "ts": "2026-06-30T19:25:45Z", "hours": 0.8839, "text": "added normal chess"}, {"post_id": 19383, "ts": "2026-06-30T17:58:23Z", "hours": 0.91, "text": "added a drawing ability , added color to see the last movement of your enemy and it now turn 180° every turn."}, {"post_id": 19112, "ts": "2026-06-30T11:00:58Z", "hours": 2.1864, "text": "added game mode and additional rule for pizza chess (for example this one makes all pieces looks like pizza ( Kings opening vs King Indian defense ) and you can’t even tell what is what piece)"}, {"post_id": 19057, "ts": "2026-06-30T07:37:39Z", "hours": 8.1514, "text": "added pizza chess (my own version of chess where the left knight is replace by a pizza)"}, {"post_id": 8544, "ts": "2026-06-14T06:27:46Z", "hours": 3.6144, "text": "added a new game with gambling (not real money!)"}, {"post_id": 3536, "ts": "2026-06-06T14:59:28Z", "hours": 1.145, "text": "added a indicator for the player turn and fixed a bug with the golden hazelnut. Added visual to hazelnut fight page in the menu."}, {"post_id": 3391, "ts": "2026-06-06T09:11:28Z", "hours": 0.5789, "text": "Finished the game ! and Fixed some bugs"}, {"post_id": 3377, "ts": "2026-06-06T08:32:40Z", "hours": 0.45, "text": "polished and added texture for this game!"}, {"post_id": 3355, "ts": "2026-06-06T07:38:18Z", "hours": 1.9031, "text": "added a settings for my games that work and finished the gameplay of the game !"}, {"post_id": 3315, "ts": "2026-06-06T05:39:18Z", "hours": 1.3564, "text": "fixed hiding, look around and added a win condition and a win screen!"}, {"post_id": 2792, "ts": "2026-06-05T16:34:28Z", "hours": 1.0958, "text": "added a Hazelnut limit for the inventory, added the possibility of hiding you hazelnuts and added the number of turns"}, {"post_id": 2319, "ts": "2026-06-04T19:05:43Z", "hours": 0.7481, "text": "added movement, look around button with how many hazelnuts you found on your position!"}, {"post_id": 2258, "ts": "2026-06-04T17:29:03Z", "hours": 0.6908, "text": "added a next player button, player turn and player with controlled position!"}, {"post_id": 2137, "ts": "2026-06-04T13:52:30Z", "hours": 0.6044, "text": "added a tile map with random tile (forest, plain or lake) for one of the game"}, {"post_id": 2037, "ts": "2026-06-04T10:08:21Z", "hours": 0.5872, "text": "Added the main page with buttons and a scroll bar"}]...
|
Title: ALL of the board games I like or Invented b Title: ALL of the board games I like or Invented by @Nezruceuil | Stardance
Devlogs:
Added the main page with buttons and a scroll bar
added a tile map with random tile (forest, plain or lake) for one of the game
added a next player button, player turn and player with controlled position!
added movement, look around button with how many hazelnuts you found on your position!
added a Hazelnut limit for the inventory, added the possibility of hiding you hazelnuts and added the number of turns
fixed hiding, look around and added a win condition and a win screen!
added a settings for my games that work and finished the gameplay of the game !
polished and added texture for this game!
Finished the game ! and Fixed some bugs
added a indicator for the player turn and fixed a bug with the golden hazelnut. Added visual to hazelnut fight page in the menu.
added a new game with gambling (not real money!)
added pizza chess (my own version of chess where the left knight is replace by a pizza)
added game mode and additional rule for pizza chess (for example this one makes all pieces looks like pizza ( Kings opening vs King Indian defense ) and you can’t even tell what is what piece)
added a drawing ability , added color to see the last movement of your enemy and it now turn 180° every turn.
added normal chess
added draw to the chess games and a options menu (that doesn’t do anything yet)
added Drawback Chess with 15 drawbacks!
added a resign button 5 more unique drawback and fixed bugs
fixed bugs with some drawbacks and added 5 more uniques drawbacks
Added ‘Pieces Fit,’ a game where everyone is given the same randomly generated custom piece and must place as many copies of it as possible without any of them attacking each other.
added a classic version to pieces fit!
added color pickers to the options menu.
Started adding an AI for Hazelnut Fight, fixed bugs with castling in the chess games, added a page to play classic chess variants, and a page where you can see all the chess variants coming in the next devlog. I also added an autofill that checks for similarity to fill in variant names (for example: aTuMyk -> Atomic .)
added every variants( Atomic,Duck Chess,Fog Of War,Giveaway,King Of The Hill,Capture Anything,Torpedo,No Castling,Ghost Board,Sideways ) and added Connect 4
fixed a bug with duck chess and added Shōgi.
fixed bugs and added Chaturaji
added an explanation for every game.
added 4 player Chess, fixed bugs and added a boat for Chaturaji.
added bigger tic tac toe and fixed bugs.
Fixed bugs and added Reversi!
fixed bugs and added more chess variants
started added talf game( ≈ 85% done) still need to add the king capture feature
finished making talf games (added the king suffocation mechanic) !
fixed bugs and added Go!
fixed a bug with chess and added lines of action!
added an explanation for every games and fixed bugs with the lines of action game.
added an arrow drawing ability and fixed bugs.
fixed bugs, improved design and added a delete drawings and arrows function win delete. (a lot of arrows + delete button = no more arrows)
Added better panel for the main page (added a Summer panel a Winter panel that you can get by being on the game at the right season. and a star dance panel that you can get by typing “Stardance”. (I drew all of the panels myself.)) , added a Save and Load function, change all of the text font to be better and added a script to see the panel (control/option space).
Fixed bugs, added a Hazelnut fight ai (not entirely done) and added more drawbacks to drawback chess.
fixed Bugs, added 2 new chess variants and Added 15 unique drawbacks to drawback chess!
Fixed Bugs (tafl,drawback chess,ect…), Added a Spring panel, Added Amazons and Added Castle War (my own strategy game).
Fixed more bugs (tafl and text in wrong place.) and improved Castle War and added some art to it.
Improve my board game, fixed bugs, added a texture for the walls, added a play time estimation for every games, added Amazons and added Gomoku.
Fixed bugs, improved my game, added recommendation, added a way to order the games however you want, added art for amazons game fire and added a Chess panel.
Fixed Bugs and added new secrets panels.
Ships:
I coded a Game in which you can play all of my favorite board games and the games that I invented. I deepened my Godot coding knowledge by recreating complicated games like: Chess, Reversi, Go, Tafl and Shōgi. I really enjoyed doing this project and now you can enjoy it too! hey, if you write **Stardance** in the background texture you can get this amazing background ↓. ✨...
|
1792853378.4869957
|
1792939778.4869957
|
|
32533
|
Kobayashi OS by @Riko | Stardance
|
Riko
|
2
|
151.0
|
2155
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-06T13:18:06Z", "hours": 108.0, "multiplier": 19.95, "payout": 2155, "blessing": "neutral", "text": "What did you make? Kobayashi OS — a web OS themed around Miss Kobayashi’s Dragon Maid . I wanted to make one of the most feature-rich and polished web OS experiences possible, something that actually feels like you’re using a PC. And yeah, this is Kobayashi’s PC, so don’t be surprised if you find something interesting related to her. The main thing I focused on was making everything feel connected, just like a real OS. Apps don’t just exist separately — Camera connects to Gallery and Files, downloads go into Files, Kanvas can save things to Files, widgets remember where you put them, and Kobayashi-chan can actually interact with the system. Basically, it’s not just a collection of apps. It’s one big little OS. What was challenging? Interconnecting all the apps and keeping everything persistent was definitely a challenge. I wanted things to behave like an actual OS instead of just being separate demos. Making the Kobayashi-chan AI work was one of the toughest hurdles. Getting tool calling, memory, emotions, and system interaction to work together took a lot of trial and error. Designing and building the widget system was another tough one. Making widgets draggable, positionable, persistent, and actually useful took quite a bit of work. Honestly, getting all the small details and polish right took way more time than I expected. What am I proud of? First — Kobayashi-chan AI. This is probably my favorite part of the whole project. She has tool calling, memory, emotions, and can actually interact with the OS. I genuinely didn’t expect I would be able to make something like this. It’s basically a little Jarvis, but Kobayashi-chan. The piano. I spent way more time on this than I probably should have. Not building it — playing it. It’s such a stupidly fun little feature that I keep losing track of time playing on it. The overall polish. I’ve been working on this for so long that I’ve honestly lost count of all the little things I added. I designed the UI in Figma, made a lot of the icons myself, kept redesigning things, learned a ridiculous amount along the way, and kept adding small details until it finally started feeling like a real OS. I’m genuinely proud of how it turned out. There are a lot of things packed into this project, and I think the best way to understand the amount of work that went into it is to actually explore it yourself. What should you know before testing? The project: https://kobayashi.cawai.site/ Please explore the widgets. This is one of the parts I spent a lot of time on. Please try the piano. Seriously. On the first load, some images may take 1–2 seconds to appear. Give it a moment. Wallpaper customization, live wallpapers, and icon customization may also take a little time on the first load. There are a LOT of games: Thor, Spider-Man, Minecraft, etc. If you open one and see a black screen, don’t panic. Some of them just take a little while to load. Come on, we’re talking about Minecraft here. Give it some time. And obviously, click around. Open things. Break things. Try stuff. A lot of the fun is finding the little details hidden around the OS. That’s Kobayashi OS. I started with the idea of making a web OS, but somewhere along the way it turned into this huge little world with its own apps, games, music, memories, AI, and way too many tiny details. Hope you have fun exploring it.", "repo": "https://github.com/anshumitasahu/Kobayashi--OS", "demo": "https://kobayashi.cawai.site/"}, {"ship_num": 2, "ts": "2026-08-01T08:57:08Z", "hours": 43.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "Kobayashi OS v1.0 is finally live! Kobayashi OS is a lightweight, anime-inspired Web Operating System built to bring a desktop-like experience to the browser. This first release focuses on a smooth window management system, allowing multiple instances of the same application, along with wallpaper, icon, and brightness customization. It uses React, TypeScript, Bun, Tailwind CSS, Zustand, and Local Storage to provide a persistent experience. The custom logo and launch banner were also designed specifically for this release. Some applications currently contain placeholder alerts and are still under development, with more features and apps planned for future updates. Highlights Anime-inspired light theme Interactive multi-window desktop Multiple instances of the same app Wallpaper, icon & brightness customization Zustand + Local Storage More apps and features coming soon", "repo": "https://github.com/anshumitasahu/Kobayashi--OS", "demo": "https://kobayashi.cawai.site/"}]...
|
[{"post_id": 62884, "ts": "202 [{"post_id": 62884, "ts": "2026-09-06T12:36:45Z", "hours": 1.8994, "text": "Polished the UI of the widgets, improved wallpaper customization, refined the app UX, resized the apps, added a bunch of new features, and made countless small tweaks across the OS. A lot has gone into this project, and I really hope it becomes the star project considering how much time I’ve poured into it. 🥹 Still more to come!"}, {"post_id": 62379, "ts": "2026-09-05T18:24:34Z", "hours": 1.5711, "text": "Kobayashi-chan is here! Chat, open apps, change settings. She remembers, reacts, and doesn’t mind a little flirting. :) Major changes!! A summary of everything I’ve done over the past 5 days. Lots of design work too—especially Figma stuff. StarDance doesn’t log my Figma hours though… too bad."}, {"post_id": 62033, "ts": "2026-09-05T07:58:56Z", "hours": 10.0164, "text": "Damnn!! Been a while haven’t posted anything bout my Work!!!!!! added a piano added a lots of Fun games improved the architecture and UI/UX improved the response of the Kobai added Terminal and Code editor.. added a proper and nice widget system changed the whole UI of my apps_ and more features are coming soon!!!!!"}, {"post_id": 58672, "ts": "2026-08-31T05:42:22Z", "hours": 9.2753, "text": "Added a lots of appppss!!! embeded few games, hackclub and typingtest.com … and Yeah added my own built apps (Json Lab , DevScope, and MoneyTrail)"}, {"post_id": 55183, "ts": "2026-08-26T18:06:02Z", "hours": 3.21, "text": "Another day on Kobayashi OS. Damn, I really turned the AI Assistant into what I actually wanted it to be. And yeah, now the Shorts app isn’t just some mere app with a icon sitting there — it actually has Shorts embedded hehe. And yeah… there’s a Kanvas app now too lol."}, {"post_id": 54549, "ts": "2026-08-25T16:00:46Z", "hours": 9.8811, "text": "Damn !! Been a while !! Yup was working on the assistant.. yeah which actually works!!! changed the architecture of ai chatbot and yeah its much better now !! and Yeah some more tweaks ongoing"}, {"post_id": 52981, "ts": "2026-08-23T11:05:21Z", "hours": 1.2497, "text": "Making the UI of music player nicer .."}, {"post_id": 52917, "ts": "2026-08-23T09:19:23Z", "hours": 3.9803, "text": "BIG ALERT IN KOBAYASHI OS I SUCCESSFULLY BUILT AN AI ASSISTANT FOR MY WEB OS! 🎉 It can actually understand commands and interact with the OS — opening apps, handling actions, and more.Another huge step for Kobayashi OS. I’m learning a LOT about AI while building this, and it’s honestly crazy seeing everything come together."}, {"post_id": 52189, "ts": "2026-08-22T08:54:28Z", "hours": 5.2606, "text": "Worked on the adding more apps.. with functionality and zero css hehe but yeah add it later currently working on a ai chat box on my project apps"}, {"post_id": 51173, "ts": "2026-08-20T17:52:30Z", "hours": 3.4503, "text": "Today was more focused on research and architecture than coding, but I still made some solid progress. I perfected the widgets, made the right-click menu fully functional, and built a basic music player that’s already working. I’ll be focusing on the CSS and visual polish for the music player tomorrow. Overall, another productive day on Kobayashi OS."}, {"post_id": 50444, "ts": "2026-08-19T16:37:20Z", "hours": 3.5219, "text": "Spent some more time working on the Widgets section 👀 Made the widgets draggable and closable only when the widgets menu the widgets top bar is being displayed.Also fixed up some CSS issues, added a right-click context menu, and added an Open option. So far, only one widget is actually openable; the rest will come later 😅 That’s enough UI work for today though — my shoulders and back are officially protesting. 💀 More to come!"}, {"post_id": 50174, "ts": "2026-08-19T09:00:29Z", "hours": 1.9064, "text": "Update Made a few solid improvements to the widget experience: Widget positions are now stored globally and persisted in local storage, so the app restores exactly where you left it after a refresh. Tweaked the visuals for a cleaner, more polished look. Added resizable widgets for a more flexible, customizable layout. Small changes, but they make the overall experience feel much smoother. More to come!"}, {"post_id": 49755, "ts": "2026-08-18T17:54:09Z", "hours": 3.4989, "text": "A little update on the Widgets section It’s already looking pretty cool , but I’m not quite done with it yet. There are still a few tweaks and some changes I want to make to how the widget system works under the hood. The goal is to make the whole experience feel cleaner, more flexible, and easier to build on as I keep adding more widgets. For now, though… I’m pretty happy with how it’s coming together. 🚀More tweaks incoming."}, {"post_id": 49478, "ts": "2026-08-18T10:44:19Z", "hours": 5.8236, "text": "Yahooooooo! Made some awesome progress! Added the Widgets feature Added a toggle function to open/close the Widgets list Added a Calendar widget Added a Clock widget Added toggle functionality for opening and closing widgets So happy to see it all coming together! 😄 🔥One step at a time, and the project is getting better and better! ✨ 💪"}, {"post_id": 48289, "ts": "2026-08-16T16:27:18Z", "hours": 3.5703, "text": "Camera Improvements Fixed a few bugs and made some nice improvements to the camera flow. Now, the last captured photo is shown directly in the camera. Tap it, and the gallery opens with the image displayed at full width and height. From there, you can swipe through the rest of your photos. Still have a few things I want to tweak in this flow, but the experience is already feeling much smoother. Next up: widgets !!"}, {"post_id": 47551, "ts": "2026-08-15T15:04:05Z", "hours": 7.6117, "text": "Been a while! Added a working camera + gallery and implemented IndexedDB for storing captured photos.Honestly, the camera logic and IndexedDB were the biggest challenges since I knew nothing about them before this. Learned from internet resources, experimented, broke things, fixed them… and finally cracked it."}, {"post_id": 44158, "ts": "2026-08-09T18:47:51Z", "hours": 3.5058, "text": "I worked on improving the app bar behavior in Kobayashi OS. I added the Menu app icon to the apps bar whenever it’s opened, and also implemented position shifting for existing apps so they adjust automatically when a new app appears. Small interaction improvements, but they make the desktop feel more dynamic and consistent."}, {"post_id": 43954, "ts": "2026-08-09T12:32:36Z", "hours": 4.1286, "text": "Gallery is successfully made! Glad it turned out well. The main challenge was the UI 🙄, but yeah, it now has Next/Previous and Back functionality. The selected image expands to the full width and height—just like we see in a proper gallery, without appearing in some corner or another section. Overall, pretty happy with how it turned out!"}, {"post_id": 43099, "ts": "2026-08-08T05:43:08Z", "hours": 7.2944, "text": "It’s been a while since my last update, but I’m happy to say Kobayashi OS is getting closer to the finish line! 🚀 Update: Added a toggle to open/close the Menu app Improved the Settings & overall UI Added app resizing App bar now hides when the screen is maximized Added a background for active page in settings Changed the About us Section More apps and features coming soon!🔥"}, {"post_id": 41836, "ts": "2026-08-06T09:25:46Z", "hours": 1.5522, "text": "Worked on the menu app UI. It’s not fully functional yet, but I’ve added some basic functionality. I think it’s looking pretty good so far. This isn’t the final version, but the overall design will probably stay pretty close to this."}, {"post_id": 41800, "ts": "2026-08-06T07:15:41Z", "hours": 4.815, "text": "Worked on the icon styling and UI, and implemented the app menu with close functionality. What ended up taking most of my time was a tiny bug—I was referencing a value as a string in one place while providing it as an object in another. It took me hours to spot the mismatch. Funny how the smallest errors can consume the most time."}, {"post_id": 40844, "ts": "2026-08-04T17:03:57Z", "hours": 3.7994, "text": "I’ve been working on the app’s UI in the maximized state. Initially, the main challenges were around the overall workflow and approach. After experimenting with a few different directions, everything clicked and I landed on this design. Next, I’ll be refining the icon styles further."}, {"post_id": 40140, "ts": "2026-08-03T15:22:36Z", "hours": 4.1178, "text": "I updated the minimize/maximize buttons so restored windows remember their previous position via the global store. Minimized apps now show a dot indicator at the bottom instead of the top. I also fixed a topbar bug where its z-index and absolute positioning caused maximized windows to get stuck underneath. My main hurdle right now is UI scaling: the window container maximizes fine, but the internal components don’t resize with it. I’m weighing a few fixes to see which approach works best."}, {"post_id": 39349, "ts": "2026-08-02T08:39:42Z", "hours": 0.9731, "text": "Just fixed the README"}, {"post_id": 38957, "ts": "2026-08-01T16:01:53Z", "hours": 2.0964, "text": "Implemented minimize and restore with functionality and the maximize feat will be implemented in the 2nd version.."}, {"post_id": 38729, "ts": "2026-08-01T08:51:37Z", "hours": 1.1942, "text": "Fixed the README and got it ready for the first official launch. The main issue was that the images and screenshots weren’t displaying correctly. Since it was a GitHub README, every small change required a commit and push just to verify whether the images loaded properly, which made the process slower than expected. Despite that, the final README turned out well and is now ready for launch."}, {"post_id": 38361, "ts": "2026-07-31T18:19:40Z", "hours": 3.6125, "text": "Added a notes app and added loading and welcome page .. Creating the logo was a bit hard :< More feats and apps are coming!!"}, {"post_id": 38107, "ts": "2026-07-31T10:07:15Z", "hours": 3.5414, "text": "Worked on the icons bar and added a new feature to the Settings app — you can now switch icon styles. Also saved wallpaper, brightness, and icon styles to localStorage . Biggest challenges were digging through a massive icon file, organising them, and getting the UI right. At one point the icons just wouldn’t change… turns out I forgot to call the function. 😅 Moving on to more features and apps."}, {"post_id": 37465, "ts": "2026-07-30T07:17:09Z", "hours": 1.4686, "text": "Updated the app icons — they look so much better now. The old ones felt a bit off. Funny disaster though: right after the change, nothing would open. Threw in some console.log()s … and the system crashed. Turns out the old icon was still being called in the main code, which made every window try to open. 😂"}, {"post_id": 37085, "ts": "2026-07-29T15:46:45Z", "hours": 2.3036, "text": "Very small visible change this time. Added functionality to the settings sidebar . Yeah thinking bout the architecture!! I did added different colors to my app background then removed it due to change in the architecture. Working on something which looks like a small change 🥲 but needs a solid time and architecture 🫠 😮💨."}, {"post_id": 36496, "ts": "2026-07-28T17:20:36Z", "hours": 2.3367, "text": "Nothing!! Just refactored the UI of the Settings app. Added a side bar with the search bar. The main headache was the Architecture !! Damn!! My head hurts !! Still moving on. I will update the full functionality of settings app next time !! (That’s my goal) . But yeah will add the other features later as the OS grows."}, {"post_id": 36275, "ts": "2026-07-28T08:28:19Z", "hours": 1.7669, "text": "Just dropped a fresh update for my web OS, Kobayashi OS ! It can now fully customize the desktop’s wallpaper with solid colors and gradients , all powered by a global store so everything stays smooth and in sync. And Yo!! not gonna lie, building this took me on a absolute emotional roller coaster. I hit bug after bug, spent ages searching for answers, only to discover the culprits were… simple typos and missed JavaScript punctuation!! Classic! But hey, every bug fixed is a lesson learned. I’m on a roll , so expect more updates super soon! Next up on the build list: making the Settings app feel like a real-deal OS and dropping some brand-new apps."}, {"post_id": 35678, "ts": "2026-07-27T08:43:11Z", "hours": 2.6417, "text": "I recently refactored our z-index management from local storage to a global system. Naturally, it brought a wave of visual bugs along with it—fix one, and four more pop up!After a solid debugging session with console.log(), everything is stable and working as expected. Next up: building out the Settings app to make it fully functional."}, {"post_id": 34138, "ts": "2026-07-24T17:18:12Z", "hours": 1.5758, "text": "Cleaned up the codebase, fixed a minor bug, and finally built a manual wallpaper changer. Decided to use global state for it and… wow. It actually clicked. I was stuck for a minute overthinking how the workflow should look, but once I just started typing, it made total sense. State management isn’t nearly as painful as I used to think. Naturally, it didn’t work on the first try—the wallpaper wasn’t updating at all. Turns out I forgot a set of parentheses and had a stupid typo. Classic. Fixed that, and now it’s smooth sailing."}, {"post_id": 33969, "ts": "2026-07-24T10:18:51Z", "hours": 2.7989, "text": "I started moving my app state from local state to a global store using Zustand. Right now, the open/close state of applications is managed globally, and next I’ll move the window dragging logic there as well. The dragging system already works locally, so this is mostly a refactor to make the OS easier to scale.I also added a Settings app, although it doesn’t do anything yet. I decided to focus on getting the core OS functionality solid before adding more features and applications. Once that’s done, I’ll add things like manually changeable wallpapers and expand the Settings app. As someone who’s still new to state management, I had no idea how to use a global store when I started. So before writing code, I spent time learning Zustand, understanding how global state works, and planning the architecture instead of jumping straight into implementation.There were plenty of moments where I got stuck. I had a few headaches trying to wrap my head around the concepts, but I kept going until it finally clicked. No big visual changes, but I think I crossed a small milestone—one that’s going to make everything I build next much cleaner and easier to manage."}, {"post_id": 31816, "ts": "2026-07-20T14:35:36Z", "hours": 1.2033, "text": "Fixed a sneaky LocalStorage bug today! 🛠️ I initially put JSON.stringify directly inside useState , which completely crashed the app and prevented it from opening. Rewrote the logic by moving JSON.stringify into a useEffect hook with tasks as a dependency. That got it working, but refreshing the page triggered a new bug: local storage was getting overwritten with an empty array. I logged the data flow at every stage and caught the issue: useEffect was running on initial mount before state was restored. Added a simple length check (tasks.length > 0) before stringifying, and everything works perfectly now! Always check your mount cycles! 🚀"}, {"post_id": 31692, "ts": "2026-07-20T09:40:11Z", "hours": 6.9306, "text": "It’s been a while since my last update, so here is what I’ve been building and refining lately: UI Refactoring: Invested a significant amount of time redesigning and polishing the UI (still a work in progress, but looking much better!). Window Management: Added support for opening, minimizing, and closing windows. Calculator App: Cleaned up the codebase and added new functionality. Task Tracking: Added a strike-through feature for completed items in To-Dos App. Next up: Implementing localStorage so task completions and window states persist across sessions."}, {"post_id": 27792, "ts": "2026-07-13T17:37:49Z", "hours": 1.6667, "text": "Focus was the window manager. Completed Implemented dynamic z-index so the active window is always brought to the front. Added window minimization.Implemented restore functionality from the Apps Bar. Preserved window state while minimized instead of closing the application. Challenge: Initially, minimizing a window removed it from the UI, but there was no way to bring it back because clicking the app icon simply ignored already opened apps. Solution: Instead of treating minimized windows as closed, I introduced a minimized state for each window. The Apps Bar now checks whether an app is already open, minimized, or closed, and performs the appropriate action—open, restore, or bring it to the front. This keeps the component mounted and preserves its state while hidden. The desktop now supports opening, closing, dragging, minimizing, restoring, and proper window stacking. With these core interactions in place, the next milestone is maximize/restore, followed by window resizing and a global state refactor with Zustand."}, {"post_id": 27668, "ts": "2026-07-13T13:16:12Z", "hours": 2.5236, "text": "Continued building the window management system for Kobayashi OS. Implemented draggable application windows from scratch using React mouse events (mousedown, mousemove, mouseup) with proper drag offsets for smooth movement. Refactored the layout to introduce a dedicated desktop area, keeping windows constrained within the workspace instead of the entire page. Spent most of the session debugging positioning logic, DOM measurements, and boundary constraints. The draggable window system is now functional and forms the foundation for upcoming features like minimize, maximize, focus (z-index), and Zustand-powered window state management."}, {"post_id": 27273, "ts": "2026-07-12T18:23:14Z", "hours": 1.3567, "text": "Continued refining the Kobayashi OS interface.Finalized the built-in app icons and continued shaping the application dock and overall desktop structure to make the OS feel more like a complete desktop environment.Built the first built-in application—a basic calculator—with support for standard arithmetic operations. This marks the beginning of adding functional utilities to the OS.The focus remains on establishing a solid foundation before expanding into more advanced applications and features."}, {"post_id": 27129, "ts": "2026-07-12T12:31:10Z", "hours": 1.3028, "text": "Continued working on the Kobayashi OS layout. The overall design is still evolving, and I’m experimenting with different arrangements before settling on a final structure. I added icons for the built-in applications and started shaping how the interface should feel. No application logic yet—right now the focus is on finding the right layout before building the functionality."}, {"post_id": 27073, "ts": "2026-07-12T09:23:01Z", "hours": 1.6333, "text": "DevDock is no longer just a collection of developer tools—it’s being redesigned into Kobayashi OS , a browser-based operating system built for developers . This update focuses on restructuring the application’s architecture, replacing the original layout with an OS-inspired interface, and laying the groundwork for applications, tools, and a desktop-like workflow. The goal is to create a cohesive environment where developer utilities feel like native applications rather than separate web pages.The transition is still in progress, but the foundation for Kobayashi OS is now replacing the original DevDock vision."}, {"post_id": 25205, "ts": "2026-07-09T09:44:08Z", "hours": 3.5269, "text": "DevLog 1 — DevDock After wrapping up DevScope, I wanted to build something that solves a problem. Introducing DevDock — a workspace that brings essential developer tools together in one place, reducing context switching and making common development tasks faster and more convenient.I’ve already finished the initial landing page. It took far more CSS work than I’m used to, but I’m happy with where it stands for now. Of course, nothing is ever truly finished—I’ll keep refining it if future feedback or better ideas come up.This is only the beginning. The real work starts now as I begin building the tools that will make DevDock genuinely useful."}]...
|
Title: Kobayashi OS by @Riko | Stardance
Devlogs: Title: Kobayashi OS by @Riko | Stardance
Devlogs:
DevLog 1 — DevDock After wrapping up DevScope, I wanted to build something that solves a problem. Introducing DevDock — a workspace that brings essential developer tools together in one place, reducing context switching and making common development tasks faster and more convenient.I’ve already finished the initial landing page. It took far more CSS work than I’m used to, but I’m happy with where it stands for now. Of course, nothing is ever truly finished—I’ll keep refining it if future feedback or better ideas come up.This is only the beginning. The real work starts now as I begin building the tools that will make DevDock genuinely useful.
DevDock is no longer just a collection of developer tools—it’s being redesigned into Kobayashi OS , a browser-based operating system built for developers . This update focuses on restructuring the application’s architecture, replacing the original layout with an OS-inspired interface, and laying the groundwork for applications, tools, and a desktop-like workflow. The goal is to create a cohesive environment where developer utilities feel like native applications rather than separate web pages.The transition is still in progress, but the foundation for Kobayashi OS is now replacing the original DevDock vision.
Continued working on the Kobayashi OS layout. The overall design is still evolving, and I’m experimenting with different arrangements before settling on a final structure. I added icons for the built-in applications and started shaping how the interface should feel. No application logic yet—right now the focus is on finding the right layout before building the functionality.
Continued refining the Kobayashi OS interface.Finalized the built-in app icons and continued shaping the application dock and overall desktop structure to make the OS feel more like a complete desktop environment.Built the first built-in application—a basic calculator—with support for standard arithmetic operations. This marks the beginning of adding functional utilities to the OS.The focus remains on establishing a solid foundation before expanding into more advanced applications and features.
Continued building the window management system for Kobayashi OS. Implemented draggable application windows from scratch using React mouse events (mousedown, mousemove, mouseup) with proper drag offsets for smooth movement. Refactored the layout to introduce a dedicated desktop area, keeping windows constrained within the workspace instead of the entire page. Spent most of the session debugging positioning logic, DOM measurements, and boundary constraints. The draggable window system is now functional and forms the foundation for upcoming features like minimize, maximize, focus (z-index), and Zustand-powered window state management.
Focus was the window manager. Completed Implemented dynamic z-index so the active window is always brought to the front. Added window minimization.Implemented restore functionality from the Apps Bar. Preserved window state while minimized instead of closing the application. Challenge: Initially, minimizing a window removed it from the UI, but there was no way to bring it back because clicking the app icon simply ignored already opened apps. Solution: Instead of treating minimized windows as closed, I introduced a minimized state for each window. The Apps Bar now checks whether an app is already open, minimized, or closed, and performs the appropriate action—open, restore, or bring it to the front. This keeps the component mounted and preserves its state while hidden. The desktop now supports opening, closing, dragging, minimizing, restoring, and proper window stacking. With these core interactions in place, the next milestone is maximize/restore, followed by window resizing and a global state refactor with Zustand.
It’s been a while since my last update, so here is what I’ve been building and refining lately: UI Refactoring: Invested a significant amount of time redesigning and polishing the UI (still a work in progress, but looking much better!). Window Management: Added support for opening, minimizing, and closing windows. Calculator App: Cleaned up the codebase and added new functionality. Task Tracking: Added a strike-through feature for completed items in To-Dos App. Next up: Implementing localStorage so task completions and window states persist across sessions.
Fixed a sneaky LocalStorage bug today! 🛠️ I initially put JSON.stringify directly inside useState , which completely crashed the app and prevented it from opening. Rewrote the logic by moving JSON.stringify into a useEffect hook with tasks as a dependency. That got it working, but refreshing the page triggered a new bug: local storage was getting overwritten with an empty array. I logged the data flow at every stage and caught the issue: useEffect was running on initial mount before state was restored. Added a simple length check (tasks.length > 0) before stringifying, and everything works perfectly now! Always check your mount cycles! 🚀
I started moving my app state from local state to a global store using Zustand. Right now, the open/close state of applications is managed globally, and next I’ll move the window dragging logic there as well. The dragging system already works locally, so this is mostly a refactor to make the OS easier to scale.I also added a Settings app, although it doesn’t do anything yet. I decided to focus on getting the core OS functionality solid before adding more features and applications. Once that’s done, I’ll add things like manually changeable wallpapers and expand the Settings app. As someone who’s still new to state management, I had no idea how to use a global store when I started. So before writing code, I spent time learning Zustand, understanding how global state works, and planning the architecture instead of jumping straight into implementation.There were plenty of moments where I got stuck. I had a few headaches trying to wrap my head around the concepts, but I kept going until it finally clicked. No big visual changes, but I think I crossed a small milestone—one that’s going to make everything I build next much cleaner and easier to manage.
Cleaned up the codebase, fixed a minor bug, and finally built a manual wallpaper changer. Decided to use global state for it and… wow. It actually clicked. I was stuck for a minute overthinking how the workflow should look, but once I just started typing, it made total sense. State management isn’t nearly as painful as I used to think. Naturally, it didn’t work on the first try—the wallpaper wasn’t updating at all. Turns out I forgot a set of parentheses and had a stupid typo. Classic. Fixed that, and now it’s smooth sailing.
I recently refactored our z-index management from local storage to a global system. Naturally, it brought a wave of visual bugs along with it—fix one, and four more pop up!After a solid debugging session with console.log(), everything is stable and working as expected. Next up: building out the Settings app to make it fully functional.
Just dropped a fresh update for my web OS, Kobayashi OS ! It can now fully customize the desktop’s wallpaper with solid colors and gradients , all powered by a global store so everything stays smooth and in sync. And Yo!! not gonna lie, building this took me on a absolute emotional roller coaster. I hit bug after bug, spent ages searching for answers, only to discover the culprits were… simple typos and missed JavaScript punctuation!! Classic! But hey, every bug fixed is a lesson learned. I’m on a roll , so expect more updates super soon! Next up on the build list: making the Settings app feel like a real-deal OS and dropping some brand-new apps.
Nothing!! Just refactored the UI of the Settings app. Added a side bar with the search bar. The main headache was the Architecture !! Damn!! My head hurts !! Still moving on. I will update the full functionality of settings app next time !! (That’s my goal) . But yeah will add the other features later as the OS grows.
Very small visible change this time. Added functionality to the settings sidebar . Yeah thinking bout the architecture!! I did added different colors to my app background then removed it due to change in the architecture. Working on something which looks like a small change 🥲 but needs a solid time and architecture 🫠 😮💨.
Updated the app icons — they look so much better now. The old ones felt a bit off. Funny disaster though: right after the change, nothing would open. Threw in some console.log()s … and the system crashed. Turns out the old icon was still being called in the main code, which made every window try to open. 😂
Worked on the icons bar and added a new feature to the Settings app — you can now switch icon styles. Also saved wallpaper, brightness, and icon styles to localStorage . Biggest challenges were digging through a massive icon file, organising them, and getting the UI right. At one point the icons just wouldn’t change… turns out I forgot to call the function. 😅 Moving on to more features and apps.
Added a notes app and added loading and welcome page .. Creating the logo was a bit hard :< More feats and apps are coming!!
Fixed the README and got it ready for the first official launch. The main issue was that the images and screenshots weren’t displaying correctly. Since it was a GitHub README, every small change required a commit and push just to verify whether the images loaded properly, which made the process slower than expected. Despite that, the final README turned out well and is now ready for launch.
Implemented minimize and restore with functionality and the maximize feat will be implemented in the 2nd version..
Just fixed the README
I updated the minimize/maximize buttons so restored windows remember their previous position via the global store. Minimized apps now show a dot indicator at the bottom instead of the top. I also fixed a topbar bug where its z-index and absolute positioning caused maximized windows to get stuck underneath. My main hurdle right now is UI scaling: the window container maximizes fine, but the internal components don’t resize with it. I’m weighing a few fixes to see which approach works best.
I’ve been working on the app’s UI in the maximized state. Initially, the main challenges were around the overall workflow and approach. After experimenting with a few different directions, everything clicked and I landed on this design. Next, I’ll be refining the icon styles further.
Worked on the icon styling and UI, and implemented the app menu with close functionality. What ended up taking most of my time was a tiny bug—I was referencing a value as a string in one place while providing it as an object in another. It took me hours to spot the mismatch. Funny how the smallest errors can consume the most time.
Worked on the menu app UI. It’s not fully functional yet, but I’ve added some basic functionality. I think it’s looking pretty good so far. This isn’t the final version, but the overall design will probably stay pretty close to this.
It’s been a while since my last update, but I’m happy to say Kobayashi OS is getting closer to the finish line! 🚀 Update: Added a toggle to open/close the Menu app Improved the Settings & overall UI Added app resizing App bar now hides when the screen is maximized Added a background for active page in settings Changed the About us Section More apps and features coming soon!🔥
Gallery is successfully made! Glad it turned out well. The main challenge was the UI 🙄, but yeah, it now has Next/Previous and Back functionality. The selected image expands to the full width and height—just like we see in a proper gallery, without appearing in some corner or another section. Overall, pretty happy with how it turned out!
I worked on improving the app bar behavior in Kobayashi OS. I added the Menu app icon to the apps bar whenever it’s opened, and also implemented position shifting for existing apps so they adjust automatically when a new app appears. Small interaction improvements, but they make the desktop feel more dynamic and consistent.
Been a while! Added a working camera + gallery and implemented IndexedDB for storing captured photos.Honestly, the camera logic and IndexedDB were the biggest challenges since I knew nothing about them before this. Learned from internet resources, experimented, broke things, fixed them… and finally cracked it.
Camera Improvements Fixed a few bugs and made some nice improvements to the camera flow. Now, the last captured photo is shown directly in the camera. Tap it, and the gallery opens with the image displayed at full width and height. From there, you can swipe through the rest of your photos. Still have a few things I want to tweak in this flow, but the experience is already feeling much smoother. Next up: widgets !!
Yahooooooo! Made some awesome progress! Added the Widgets feature Added a toggle function to open/close the Widgets list Added a Calendar widget Added a Clock widget Added toggle functionality for opening and closing widgets So happy to see it all coming together! 😄 🔥One step at a time, and the project is getting better and better! ✨ 💪
A little update on the Widgets section It’s already looking pretty cool , but I’m not quite done with it yet. There are still a few tweaks and some changes I want to make to how the widget system works under the hood. The goal is to make the whole experience feel cleaner, more flexible, and easier to build on as I keep adding more widgets. For now, though… I’m pretty happy with how it’s coming together. 🚀More tweaks incoming.
Update Made a few solid improvements to the widget experience: Widget positions are now stored globally and persisted in local storage, so the app restores exactly where you left it after a refresh. Tweaked the visuals for a cleaner, more polished look. Added resizable widgets for a more flexible, customizable layout. Small changes, but they make the overall experience feel much smoother. More to come!
Spent some more time working on the Widgets section 👀 Made the widgets draggable and closable only when the widgets menu the widgets top bar is being displayed.Also fixed up some CSS issues, added a right-click context menu, and added an Open option. So far, only one widget is actually openable; the rest will come later 😅 That’s enough UI work for today though — my shoulders and back are officially protesting. 💀 More to come!
Today was more focused on research and architecture than coding, but I still made some solid progress. I perfected the widgets, made the right-click menu fully functional, and built a basic music player that’s already working. I’ll be focusing on the CSS and visual polish for the music player tomorrow. Overall, another productive day on Kobayashi OS.
Worked on the adding more apps.. with functionality and zero css hehe but yeah add it later currently working on a ai chat box on my project apps
BIG ALERT IN KOBAYASHI OS I SUCCESSFULLY BUILT AN AI ASSISTANT FOR MY WEB OS! 🎉 It can actually understand commands and interact with the OS — opening apps, handling actions, and more.Another huge step for Kobayashi OS. I’m learning a LOT about AI while building this, and it’s honestly crazy seeing everything come together.
Making the UI of music player nicer ..
Damn !! Been a while !! Yup was working on the assistant.. yeah which actually works!!! changed the architecture of ai chatbot and yeah its much better now !! and Yeah some more tweaks ongoing
Another day on Kobayashi OS. Damn, I really turned the AI Assistant into what I actually wanted it to be. And yeah, now the Shorts app isn’t just some mere app with a icon sitting there — it actually has Shorts embedded hehe. And yeah… there’s a Kanvas app now too lol.
Added a lots of appppss!!! embeded few games, hackclub and typingtest.com … and Yeah added my own built apps (Json Lab , DevScope, and MoneyTrail)
Damnn!! Been a while haven’t posted anything bout my Work!!!!!! added a piano added a lots of Fun games improved the architecture and UI/UX improved the response of the Kobai added Terminal and Code editor.. added a proper and nice widget system changed the whole UI of my apps_ and more features are coming soon!!!!!
Kobayashi-chan is here! Chat, open apps, change settings. She remembers, reacts, and doesn’t mind a little flirting. :) Major changes!! A summary of everything I’ve done over the past 5 days. Lots of design work too—especially Figma stuff. StarDance doesn’t log my Figma hours though… too bad.
Polished the UI of the widgets, improved wallpaper customization, refined the app UX, resized the apps, added a bunch of new features, and made countless small tweaks across the OS. A lot has gone into this project, and I really hope it becomes the star project considering how much time I’ve poured into it. 🥹 Still more to come!
Ships:
Kobayashi OS v1.0 is finally live! Kobayashi OS is a lightweight, anime-inspired Web Operating System built to bring a desktop-like experience to the browser. This first release focuses on a smooth window management system, allowing multiple instances of the same application, along with wallpaper, icon, and brightness customization. It uses React, TypeScript, Bun, Tailwind CSS, Zustand, and Local Storage to provide a persistent experience. The custom logo and launch banner were also designed specifically for this release. Some applications currently contain placeholder alerts and are still under development, with more features and apps planned for future updates. Highlights Anime-inspired light theme Interactive multi-window desktop Multiple instances of the same app Wallpaper, icon & brightness customization Zustand + Local Storage More apps and features coming soon
What did you make? Kobayashi OS — a web OS themed around Miss Kobayashi’s Dragon Maid . I wanted to make one of the most feature-rich and polished web OS experiences possible, something that actually feels like you’re using a PC. And yeah, this is Kobayashi’s PC, so don’t be surprised if you find something interesting related to her. The main thing I focused on was making everything feel connected, just like a real OS. Apps don’t just exist separately — Camera connects to Gallery and Files, downloads go into Files, Kanvas can save things to Files, widgets remember where you put them, and Kobayashi-chan can actually interact with the system. Basically, it’s not just a collection of apps. It’s one big little OS. What was challenging? Interconnecting all the apps and keeping everything persistent was definitely a challenge. I wanted things to behave like an actual OS instead of just being separate demos. Making the Kobayashi-chan AI work was one of the toughest hurdles. Getting tool calling, memory, emotions, and system interaction to work together took a lot of trial and error. Designing and building the widget system was another tough one. Making widgets draggable, positionable, persistent, and actually useful took quite a bit of work. Honestly, getting all the small details and polish right took way more time than I expected. What am I proud of? First — Kobayashi-chan AI. This is probably my favorite part of the whole project. She has tool calling, memory, emotions, and can actually interact with the OS. I genuinely didn’t expect I would be able to make something like this. It’s basically a little Jarvis, but Kobayashi-chan. The piano. I spent way more time on this than I probably should have. Not building it — playing it. It’s such a stupidly fun little feature that I keep losing track of time playing on it. The overall polish. I’ve been working on this for so long that I’ve honestly lost count of all the little things I added. I designed the UI in Figma, made a lot of the icons myself, kept redesigning things, learned a ridiculous amount along the way, and kept adding small details until it finally started feeling like a real OS. I’m genuinely proud of how it turned out. There are a lot of things packed into this project, and I think the best way to understand the amount of work that went into it is to actually explore it yourself. What should you know before testing? The project: https://kobayashi.cawai.site/ Please explore the widgets. This is one of the parts I spent a lot of time on. Please try the piano. Seriously. On the first load, some images may take 1–2 seconds to appear. Give it a moment. Wallpaper customization, live wallpapers, and icon customization may also take a little time on the first load. There are a LOT of games: Thor, Spider-Man, Minecraft, etc. If you open one and see a black screen, don’t panic. Some of them just take a little while to load. Come on, we’re talking about Minecraft here. Give it some time. And obviously, click around. Open things. Break things. Try stuff. A lot of the fun is finding the little details hidden around the OS. That’s Kobayashi OS. I started with the idea of making a web OS, but somewhere along the way it turned into this huge little world with its own apps, games, music, memories, AI, and way too many tiny details. Hope you have fun exploring it....
|
1792878436.301216
|
1793742436.301216
|
|
4967
|
slackzilla 🦖🦖 by @rylvion | Stardance
|
rylvion
|
0
|
151.14
|
0
|
[]
|
[{"post_id": 78787, "ts": "202 [{"post_id": 78787, "ts": "2026-10-11T16:34:14Z", "hours": 31.9517, "text": "devlog 11 - ADMIN EHANCEMENTS: errors views, log archiving & restoration, logs deletion controls, RAG IMPROVEMENTS AND MORE… date: 11/10/2026 time logged: 31hr 57m version: v0.9.6-beta this release focused on making Slackzilla easier to debug, manage and understand internally. a LOT of work went into the admin panel, route extraction and testing. 1. admin panel got some SERIOUS upgrades added 3 new admin pages for server events, errors and logs. also split admin.css into separate stylesheets because one giant stylesheet isn’t exactly a maintenance strategy. error monitoring added an error viewer with timestamps, stack traces and source files improved error messages to make them more user-friendly log management added a log archive viewer with backup timestamps, line counts and file sizes added backup restoration and switching added controls to delete specific lines, ranges or the last X lines added log rotation and telemetry reset controls added the /log-archives admin endpoint 7 new admin controls: delete-logs , delete-lines , delete-last , delete-line , rotate-logs , restore-log and reset-telemetry that’s a LOT more control over server logs than before. 2. endpoints & session security added 4 new endpoints : 3 admin endpoints: /errors , /errors/stream , /log-archives 1 public endpoint: /api/session /api/session now provides current session information, with csrfToken moved from /api/admin/summary to the new endpoint. also fixed the CSRF token not appearing in the document head. small bug, annoying consequences. removed the children=[] pattern from routes.js and its associated dependencies. 3. RAG / code parser improvements route extraction got a substantial upgrade. detects routes using exact path checks, prefix checks and array-based matching improved HTTP method inference for conditional routes, including GET and POST merges duplicate route entries and inferred methods expanded discovery beyond server/api/ , including /webhook and /admin/login improved /api/routes to return supported methods, match types, source files and line numbers the goal: give the RAG system more accurate route information instead of confidently retrieving incomplete or duplicated data. ᕦ(ò_óˇ)ᕤ 4. AI activity & markdown the AI activity view now displays elapsed time alongside tool summaries and responses, making it easier to follow what the system actually did. replaced the custom Markdown parser with marked AND dompurify for established parsing and HTML sanitisation. switched the configured model from openrouter/free to openai/gpt-6-luna , aiming for better performance and reliability. also deduplicated getReleaseInfo to avoid maintaining redundant version information. 5. testing added 3 npm scripts : test:code-parser for code parser tests test:rag for RAG tests test:all for the broader test suite and validation checks added automated code parser tests covering route extraction, HTTP method inference and deduplication. 6. docs updated README.md and project documentation to reflect the latest endpoints, admin controls, route extraction changes and RAG improvements. documentation staying in sync with the codebase. revolutionary concept, apparently. release summary v0.9.6-beta added: 4 endpoints 7 admin controls 3 admin pages 3 npm testing scripts improved error visibility, log management, session information and route extraction updated Markdown rendering, AI activity information and documentation the main focus was improving maintainability, debugging and the reliability of Slackzilla’s internal tooling. ( ͡° ͜ʖ ͡°) next devlog hint: a terminal is coming. think less clicking through admin pages, more interacting with Slackzilla directly through a terminal interface. details in the NEXT devlog. \(^o^)/"}, {"post_id": 71489, "ts": "2026-09-20T22:15:26Z", "hours": 27.4447, "text": "Devlog 10 - QOL DASHBOARD IMPROVEMENTS, RAG ENHANCEMENTS WITH TOOLS & CACING AND LOTS OF STUFF time logged: 27 hrs 30 mins date: 20/09/2026 version: v0.9.5-beta This devlog is COVERING A LOT of stuff that i did, but im not gonna drag this over like i did from all other devlogs so heres are the features: covering from v0.9.1-beta to v0.9.5-beta, see release-history.json for the full changelog Features added Added RAG TOOLS including getStatus - returns the status of the RAG getCommands - with enhanced filtering (filter by name, usage hint, id, category and fuzzy filter (makes it easier for RAG to find commands)) getReleaseInfo - returns the release info of the CURRENT version and (optionally) any other version (pass it by parameter) runCommand - runs a command by id getLogs - returns recent logs for the RAG to inspect (max 100, default 20) getMetrics - returns the metrics of the RAG (memory usage, cpu usage, etc) getRoutes - returns the routes of the APIs added 3 new scripts ( npm run ) which are rag - runs the RAG with or without the --ai flag to see context/answer cli rag:use-tool - runs a tool by name (with optional parameters) release - appends current release notes from package.json to release-history.json . Added new endpoints /api/telemetry - returns the telemetry history separately from the main status endpoint /api/command/:id - gets or executes a command by id /api/telemetry/stream - streams telemetry updates using SSE /api/admin/server-events - returns server events for the admin dashboard /api/ask - now uses SSE to stream the RAG’s activity ( yers now we can see the RAG in aciton ), context, tool usage, token usage and final response dashboard imporvements added a seperate admin server events page (wevent searching, filtering, prioritisation and stylying) added a command runner on the command page fixed recent activity not showing up on the dashboard Added visible AI activity to the AI page the RAG now emits activity events while processing a question AI page is cached on localStorage so you can go back and forth between pages without losing the AI page state Improved the RAG’s repository awareness added AST-based parsing using acorn and acorn-walk added persistent .rag/index.json added symbol search and dependency analysis added function, export, function call, environment variable and route extraction added richer code context and source metadata added automatic index rebuilding improved path-based dependency detection fixed the RAG detecting references created using path.join(rootDir, ...) add token budgeting and usage tracking. Changed how telemetry is handled telemetry history is no longer included in /api/status or any other endpoints CHANGED all admin endpoints so that none of them has the telemetryHistory anymore Added release history tracking added release-history.json /sz-release can now retrieve release information for different versions npm run release automatically appends the current release notes Tl;DR: RAG got a major upgrade with AST-powered repository indexing (acorn/acorn-walk), persistent indexing via .rag/index.json , symbol search, dependency analysis, function/export/call/env-var/route extraction, richer source metadata, automatic index rebuilding, 7 new RAG tools, 3 new CLI tools, SSE-powered streaming for /api/ask and telemetry, telemetry endpoints, admin server events, live AI activity tracing, token/context/tool usage visibility, AI session caching, release history tracking, telemetry separation from /api/status, admin API cleanup, and a large collection of bug fixes, repository-awareness improvements and major QOL improvements. . hopefully i will be devloging every version till i ship"}, {"post_id": 61072, "ts": "2026-09-03T19:44:37Z", "hours": 29.0225, "text": "Devlog 9: React Dashboard time logged: 29hr 1m date: 03/09/2026 The React dashboard is finally here! I completely migrated the old EJS dashboard with a new React app. THIS will be IS the biggest devlog yet, as it covers a lot of changes and new features. main features added is migration, codebase assistance, graphs, telemetary and a new api. Heres a summary of what i changed renamed src/ to bot/ - this is because its related to the bot and not the server and its not where all the main files are migrated the EJS dashboard into a React app overhauled the UI deleted the pages system and the workflow to build it added a new RAG modal that answers questions about the repo and shows related visual assets (also includes a new rag.js file that handles retrieval and generation of answers) The react dashboard has the following features: 10 pages (Home, Admin, Api, Commands, Docs, Home, Logs, AI, Sitemap, Status) 12 components 8 utilities js exports in the utils/ folder 404 page (with routing) & Error Boundary modal page public AI assistant page with repository-aware retrieval and related visual assets command API with stable IDs, command help, and captured command responses (so API can be called from the dashboard) public status, logs, metrics, command, and AI API endpoints admin feedback controls for filtering, viewing, replying, marking read/unread, and deleting (they dont work on windows only linux) CPU, memory, disk, and network telemetry with responsive graphs and hover data tooltips (so satisfying) Current React Architecture The active client source lives in server/client/src/ . Vite builds it into server/client/dist/ , which is served by server/server.js . React Router renders the page routes declared in server/client/src/components/utils/routes.js . The server still owns the HTTP status code: known browser routes receive the React shell with 200 , while unknown browser routes receive the same shell with 404 so the client can render the NotFound page without hiding the correct HTTP semantics. The old EJS dashboard and static page-generation flow are no longer the active dashboard path. The bot remains in bot/ , separate from the dashboard server, and the server uses explicit Node http handlers under server/api/ . RAG and Command APIs The new /ai page sends questions to POST /api/ask . server/lib/rag.js retrieves relevant repository text while excluding priv/ , .env files, dependencies, logs, and build output. It can identify related image assets such as the calculator flowchart without parsing binary image contents. If configured, the retrieved context is sent to the AI provider; otherwise the endpoint returns a local configuration message and source metadata. Commands are exposed through GET /api/commands , GET /api/commands/:id , and POST /api/commands/:id . The API adapter reuses the same command modules as Slack and captures their responses. The Commands page provides the interactive runner, while the AI page remains focused on repository questions. Explicit natural-language requests can still ask the RAG service to execute a recognised slash command and return its result. Limitations The migration exposed several boundaries that are now documented in docs/ : authentication and CSRF, the webhook protocol, API registration, setup, and RAG behaviour. Runtime persistence is still file-backed through server/database/store.js ; an external database remains a future adapter project rather than a required dependency. Linux systemd controls and deployment scripts are intended for the hosted environment. On Windows local development, the dashboard, APIs, telemetry, and React pages work, but system service actions and Bash deployment are not expected to succeed. The dashboard must be rebuilt and restarted after production client or server changes."}, {"post_id": 48936, "ts": "2026-08-17T14:33:48Z", "hours": 17.7958, "text": "Devlog 8 - server hosted dashboard finally happened time logged: 17hr 47min 45s date: 16/08/2026 The server hosted dashboard is finally here! It is hosted with the webhook, connected to the bot, and shows logs, status, and other information. architecture The Slackzilla server-hosted dashboard is the web control centre sitting alongside the webhook (runs on the same port). The server runs the dashboard, API routes, authentication/session handling, CSRF protection, logging, monitoring and administrative controls. The dashboard has been moving from the earlier static local/pages prototype (see devlog 7) towards an EJS-based server-rendered architecture, with shared layouts/partials and a global dashboard styling system. The server also handles the GitHub deployment webhook on port 9000 , while Nginx sits in front of the application. The dashboard is intended to expose things such as bot status, logs, statistics, monitoring, API documentation and admin controls rather than sshing into the server and running commands manually I’ve been separating the dashboard’s presentation layer, API layer and server logic. During development, I encountered and fixed several contract problems where route modules expected helpers such as context.sendError() and context.sendOk() that the runtime context did not provide. I also changed the authentication-aware API/SSE routes so unauthenticated requests return proper JSON/HTTP responses rather than accidentally redirecting an EventSource connection into an HTML login page. The dashboard has admin-only functionality, CSRF protection and server-side authentication, whilst the bot itself uses Slack’s Events/Slash Command mechanisms for incoming Slack activity. Slack’s Events API delivers subscribed events to the HTTP endpoint, while slash commands are sent to the configured app endpoint via HTTP POST. /api/admin routes are protected by an authenticated admin session and CSRF protection. Admin authentication uses a session cookie, which is only created after logging in through rylvion.hackclub.app/admin/login . The admin password is never stored in plaintext. It is stored as a PBKDF2-HMAC-SHA512 hash in the server environment, using a random salt and a high iteration count. The dashboard does not allow the password to be created or changed, so the server environment must be updated manually before restarting the service. CSRF tokens are generated server-side and sent to the authenticated client via a cookie. For POST requests, the client must return the token in the X-CSRF-Token header, preventing unauthorised or forged requests from triggering admin actions. /api/ (excluding /api/admin ) has no authentication Port 9000 is used for the webhook and the dashboard whilst the bot itself runs on socket mode. The api that currently has these endpoints base url: https://rylvion.hackclub.app/api/ GET /api/status - returns the bot status and other information GET /api/logs - returns the bot logs from slackzilla.log as JSON, including the file size in bytes POST /api/admin/control - allow the admin to control the accepts several actions such as start , stop , restart , redeploy , refresh , refresh-logs , refresh-status (self explanatory actions) with dangerous actions (such as redeploy , restart and stop ) contains a confirmation prompt to prevent accidental execution of the action. GET /api/admin/feedback - returns database feedback as JSON GET /api/admin/feedback/:id - returns the feedback with the given ID as JSON POST /api/admin/feedback/:id - allows the admin to control and accepts 3 actions such as delete , read , unread , respond . GET /api/admin/events - is a SSE (Server Sent Event) endpoint that allows the admin to receive real time events from the server such as logs, status, feedbacks and other information about the bot."}, {"post_id": 38519, "ts": "2026-07-31T22:58:19Z", "hours": 9.9792, "text": "Devlog 7 - Adding a Web Dashboard (locally) time logged: 9hr 58m date: 31/07/2026 ███████╗██╗ █████╗ ██████╗██╗ ██╗███████╗██╗██╗ ██╗ █████╗ ██╔════╝██║ ██╔══██╗██╔════╝██║ ██╔╝╚══███╔╝██║██║ ██║ ██╔══██╗ ███████╗██║ ███████║██║ █████╔╝ ███╔╝ ██║██║ ██║ ███████║ ╚════██║██║ ██╔══██║██║ ██╔═██╗ ███╔╝ ██║██║ ██║ ██╔══██║ ███████║███████╗██║ ██║╚██████╗██║ ██╗███████╗██║███████╗███████╗██║ ██║ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ added a local prototype view of the web dashboard for slackzilla, its hosted on Github Pages and is a static site that is not connected to the bot yet, it shows the design and layout of the dashboard and how it will look when its connected to the bot. the dashboard is designed to be responsive and works on both desktop and mobile devices. it has a clean and modern design that is easy to navigate. for Github Pages hosting, i used gh-pages branch which meant creating a seperate workflow ( pages.yml ) to build and deploy the dashboard to the dist/ folder. before that i needed a way to make it minified and optimised for production, so i used terser , clean-css and html-minifier-terser to minify the js, css and html files respectively, i do realise that this meant adding extra dependencies to the project (even though its not linked to the bot yet). The deployment website will be hosted on the bot’s server ( rylvion.hackclub.app ) and on the same port as the webhook server i also added new logging functionality that return smth like this the one below, this helps me track the usage of the commands and see how many times they have been used, when they were last used and by whom. this will be useful for the dashboard to show command usage statistics and manage command permissions. { \" commands \" : { \" sz-hash \" : { \" count \" : 3 , \" lastUsedAt \" : \" 2026-07-23T13:27:28.116Z \" , \" lastUser \" : \" rylvion \" , \" lastUserId \" : \" U0A84FA2NP2 \" } } } TL;DR: added a local prototype view of the web dashboard for slackzilla, its hosted on Github Pages and is a static site that is not connected to the bot yet, it shows the design and layout of the dashboard and how it will look when its connected to the bot and logging functionality that return command usage statistics and manage command permissions. introduced a new logging system that logs to both console and a file. added command usage tracking to record how often each command is used. enhanced server.js to serve dashboard, logs, and status pages. improved webhook handling and added signature verification. added a new metrics utility and feedback.js for tracking command usage statistics. next devlog will be about connecting the dashboard to the bot & server and making it functional, its gonna be a long one so stay tuned for that"}, {"post_id": 35837, "ts": "2026-07-27T15:06:51Z", "hours": 6.6219, "text": "Devlog 6 - command buffet time logged: 6hr 37m 19s date: 22/07/2026 okay since most of the major architectural refactors are done, i can finally focus on adding more commands to Slackzilla. this devlog is about the new commands that i added to the bot, which are mostly utility commands that can be used for various purposes. base64 encoding hashing - supports multiple algorithms, default: sha256 decoder - decodes every algorithm that hashing supports random ids (uuid) secure tokens random numbers unix date conversion text case conversion (camel, pascal, snake, kebab) character counter rock paper scissors magic 8ball (the only entertainment command added in this devlog) the trickier parts were the input parsers. hash has to decide whether the first word is an algorithm or part of the text, case needs to split words without mangling the content too much, and unix has to switch between timestamps and readable dates without getting confused by weird inputs. i also added guardrails for invalid ranges and missing text so the bot gives a useful reply instead of just silently failing. the fun commands were the easiest win. 8ball just picks from a response list, and rps does a quick match against the player choice and the bot choice to decide whether it was a win, lose, or tie. it is very low stakes code, which is honestly refreshing after the manifest workflow stuff from last time. all utilities commands all have a help command associated to it the current utilities commands include: /sz-base64 - encodes a string to base64 /sz-hash - generates a hash of a string, default: sha256 /sz-decode - decodes a base64 encoded string /sz-uuid - generates a random uuid /sz-token - generates a random secure token /sz-random - generates a random number in a given range /sz-unix - converts a unix timestamp to a human-readable date and vice versa /sz-case - converts a string to camelCase, PascalCase, snake_case, or kebab-case /sz-count - counts the number of characters in a string /sz-rps - plays rock paper scissors with the bot /sz-calc - evaluates mathematical expression so all of it has a 2nd argument for help, so for example /sz-hash help will show the usage and examples of the command. i also added on package.json , 2 new scripts \"validate-commands\" : \"ajv validate -s schemas/command.schema.json -d src/data/commands.json\" , \"cmd-stats\" : \"node scripts/stats.js\" one to validate the commands.json file against the command.schema.json file, and another to provide statistics about the number of commands in the commands.json file. this will help me keep track of how many commands are in the bot and also make sure that the commands.json file is valid before a pull request is merged. currently, the bot has 22 commands in total, and 11 of them are utility commands (10 of them got added into this devlog (excluding the /sz-8ball that also was added in this devlog, but is an entertainment command)). the other 11 commands are the ones that were already in the bot before this devlog. i need some feedback on what other commands i should add to the bot, so if you have any ideas, please let me know. (im running out of ideas * sighs * ) im also planning on adding a web dashboard to the bot, which will allow users to see the bot’s status, logs, and other information in a more user-friendly way. this will be a big change, so stay tuned for that in the next devlog TL;DR: added a bunch of new slash commands for Slackzilla, kept the implementations small and native where possible, and tightened up the input handling so the new tools fail a little more gracefully."}, {"post_id": 33001, "ts": "2026-07-22T15:09:05Z", "hours": 7.1047, "text": "Devlog 5 - manifest workflow time logged: 7hrs 6min 16 sec date: 21/07/2026 ok so i kinda lied in the last devlog. this ended up being another major architectural change to slackzilla. the slack manifest system was always the most brittle part of the pipeline and manually maintaining manifest.json was a guaranteed way to let things drift. today i finally automated the entire manifest workflow. the new setup makes commands.json and meta.js the single source of truth. i wrote a manifest generator that loads all metadata like descriptions colours oauth scopes bot info and merges it with the command definitions to build a complete slack manifest object. once assembled it writes manifest.json and syncs it during deployment. validation is baked directly into the generator. i wired AJV into both stages. every command is validated against command.schema.json and the final manifest is validated again against slack’s official schema. if anything fails the generator stops immediately with a clear error instead of producing a broken file. this caught several issues especially after i moved all schemas into a dedicated schemas/ directory to clean up the project structure. the github actions workflow also got a major overhaul. every push now generates the manifest validates commands and manifest rotates the slack configuration token updates github secrets syncs the manifest through the slack api and deploys the bot. changing a slash command in commands.json now updates both the bot and the slack app config automatically. no more manual edits to manifest.json or the slack app settings. the entire pipeline is now self maintaining and fully automated. token rotation was the biggest pain. slack configuration tokens only last twelve hours so storing them in github secrets was not enough. i created a fine grained github PAT with permission to modify repo secrets and wrote rotateManifestToken.js to handle the full refresh cycle. it rotates the manifest token receives a new refresh token and updates both secrets so the next deployment does not break. i spent way too long debugging an invalid_refresh_token error. note that slack’s copy button does not copy the existing refresh token it generates a new pair. every time i clicked it the previously saved token became invalid instantly. once i realised that i started updating github secrets immediately after copying the new pair so everything stayed in sync. after that the rotated token still was not being passed correctly between github actions steps. i switched to GITHUB_OUTPUT so the rotation step could hand the new token to the manifest sync step. then slack started returning not_authed which led to another rabbit hole. the manifest update endpoint requires the token in the correct auth header not the request body. once i fixed the request format the entire pipeline finally ran clean end to end. See figure 2 for github actions workflow TL;DR slackzilla now auto generates its slack manifest validates everything with AJV rotates configuration tokens syncs the manifest during deployment and updates the bot server with minimal manual work. after a lot of debugging around token rotation schema mismatches and github actions quirks the deployment pipeline is basically self maintaining except refreshing the github PAT every ninety days."}, {"post_id": 30675, "ts": "2026-07-18T13:24:53Z", "hours": 2.345, "text": "Devlog 4: calc and roll commands time logged: 2 hr 20 minutes date: 17/07/2026 today i spent some time reading the Java Shunting Yard Algorithm implementation on GeeksforGeeks . The example focuses entirely on converting an infix expression into postfix form. It scans the expression character by character, places operands directly into the output string, and uses a stack to manage operators based on precedence and associativity. Once the scan is complete, any remaining operators are appended to produce a postfix expression like 5238-52^^/+. The Java version supports operators including + , - , * , / , and ^ , and it handles parentheses, but it stops after producing the postfix representation. It does not evaluate the numerical result. after reading that, i wanted something more complete for Slackzilla. I used the same core idea of converting infix to postfix, but I extended it in several ways. My implementation tokenises the input instead of reading one character at a time, which allows it to support multi digit numbers and decimals. I added unary minus detection so expressions like -3 or 5*-2 behave correctly. One challenge was distinguishing unary minus from subtraction. The parser has to determine whether it starts with a number or subtracts two values depending on the previous token. I kept exponentiation with the correct right associativity. Once the postfix array is built, I added a second stage that evaluates the postfix expression using a number stack. This lets the function return the final computed value rather than just the postfix notation. The result is a more flexible and practical version of the algorithm that fits naturally into Slackzilla and can evaluate real expressions interactively. after finishing the calculator command, I also added another command to Slackzilla called /sz-roll . This command allows users to simulate dice rolls directly inside Slack. It supports normal dice notation like d6 , custom dice sizes like d20 , multiple dice such as 2d6 , and modifiers like 2d20+5 . I added parsing logic to split the dice expression into the amount of dice, number of sides, and any modifier before rolling each dice individually and calculating the final total. I also added a /sz-roll help and /sz-calc help command which explains how to use the command and shows examples of supported formats. also i spent A LOT of time making the flowchart to describe this more than the implementation itself. I think it will be useful for future reference and for anyone else who wants to understand how the algorithm works. i also added json schema validation for commands.json as talked in devlog 2 the commands.json is the source of truth, so i addded a validator.yml to validate the json schema of commands.json and make sure it is valid before a pull request is merged. This will help prevent errors and ensure that the commands are correctly defined. TL;DR: added a calculator using the Shunting Yard algorithm with postfix evaluation, plus a flexible dice rolling command. Also added help commands, a flowchart and automated JSON schema validation. also ill add way more devlogs from now on since most of my last devlogs were major architecture refactor, unless there is a major change, i will just add a small devlog to add a new command/small feature, fix a bug, improve an existing feature and add more documentation."}, {"post_id": 27894, "ts": "2026-07-13T20:15:52Z", "hours": 6.0267, "text": "Devlog 3 - github actions webhook request (13/07/2026) time logged: 6hr 01min i added a webhook dispatcher that automatically triggers a deployment whenever i commit to the main branch. Instead of manually pulling changes on the server and restarting the service myself, the GitHub Action now sends a signed webhook to my deployment server, which verifies it and runs the deploy script for me. i also added 2 more commands /sz-meme - this makes a meme /sz-echo [str] - this echoes the string you can see the flowchart below and see the full devlog 3 here (hyperlinked) TL;DR whenever i commit the server automatically receives it and slackzilla updates to the latest version"}, {"post_id": 21774, "ts": "2026-07-03T21:35:52Z", "hours": 11.0325, "text": "Devlog 2 - server setup & modular command system & more [03/07/2026] time logged: 10hr 59 min so since the last devlog, i’ve have worked on LOADS of things including behind the scene (this doesnt get tracked on hacaktime :( ), i dont even know if this will hit the char limit, but anyways, here are the logs EDIT: IT DID HIT THE CHAR LIMIT, turns out the original devlog was ~9.6k characters 💀 while the limit is only 4k, so this is the shortened version. the full one is still in priv/devlogs/devlog2.md if anyone wants the extended edition.z Commits logs e47c48ac0970b49015e920607a59657259122f2f - made the cur proj into more modular featuring dynamic command loading system w commands.json as the source of truth on this, i figured out that packing every single thing into one file is a bad idea, so i made a modular command system that loads commands dynamically from a commands.json file, this way, i can add new commands without having to touch the main bot.js file, and also, it makes it easier to manage commands since they are all in one place 03b07ffc6f44d0231f9073f637caee1f9437705a) - added more detailed server logging with time & user at the start of most console logs onto terminal [coloured output] this was way more of a pain than i thought it was gonna be. after countless rounds of testing i finally got it working how i wanted. the ansi colours kept either leaking into the rest of the terminal or resetting back to the default colour when i needed them to go back to the current log level. i was genuinely losing my mind because every “fix” just broke something else. after hours of messing around i ended up making a wrapper so instead of resetting after things like the username or command, it switches back to whatever the current log level colour is (info, success or error), then only resets once right at the end of the log. it took way longer than it should’ve and nearly made me give up, but it finally does exactly what i wanted and nothing leaks anymore.added src/utils/logger.js and switched almost every console.log over to it. it adds timestamps, users, commands and coloured log levels.and changed all the console logs in src/bot.js and src/cmds/ to use the new logger api b81c739cc06edba2895695e51dbbf88d8868f19f - overhauled readme well as the name implies its pretty self explanatory lol but i massively expanded the README with better documentation and setup instructions. Server setup find more info in the setup guide on Step 7 out of 8 in https://stardance.hackclub.com/missions/slack-bot/guide#step-7 i had to apply for a Nest server and get SSH access.yeah so basically my starting gear is a potato server (2GB RAM, 16GB storage) but that’s more than enough for this bot. i also got to mess around with linux for a bit ( yes i am a windows user ). honestly… i kinda liked it and i’m like 80% convinced i’ll switch one day (but too much of a hassle to switch rn, but maybe in the future). after installing everything, setting up the environment variables and creating a systemd service, i finally got the bot running 24/7.TL;DR: i got the bot running 24/7 in the server and it works perfectly fine, and i can now add new commands without having to touch the main bot.js file, and also, and added detailed server logging with time & user aight y’all i spent an ENTIRE HOUR writing this devlog and THEN i had to rewrite a shortened down version, y’all better rate me 9/9 for story telling, rylvion signing out 🫡 pls see my full devlog version"}, {"post_id": 1731, "ts": "2026-06-03T22:11:42Z", "hours": 1.815, "text": "slackzilla after countless testing of figuring out why my bot isnt working, i found out the reason… well sort of… it seems so that the bot doesnt read my messages nad only respond to app commands idk how to make it respond to my messages if it not a cmd but anyways here are the logs 7be9077de2a487f7be8a779123420295dc9bff7e - add MIT license 9ceb9c2fe638f91ab6b0a14326437c9a5f32f499 - add initial bot setup (just for testing!] 66eb775434c871f063fd57daf0bdfc7bd66501f1 - add launch.json, .env.example (like formats), README.md, add stuff to bot.js btw this isnt hosted (will host soon) or anything and to use it you have to use the cmd prefix (sc-) e.g., /sz-ping"}]...
|
Title: slackzilla 🦖🦖 by @rylvion | Stardance
Devl Title: slackzilla 🦖🦖 by @rylvion | Stardance
Devlogs:
slackzilla after countless testing of figuring out why my bot isnt working, i found out the reason… well sort of… it seems so that the bot doesnt read my messages nad only respond to app commands idk how to make it respond to my messages if it not a cmd but anyways here are the logs 7be9077de2a487f7be8a779123420295dc9bff7e - add MIT license 9ceb9c2fe638f91ab6b0a14326437c9a5f32f499 - add initial bot setup (just for testing!] 66eb775434c871f063fd57daf0bdfc7bd66501f1 - add launch.json, .env.example (like formats), README.md, add stuff to bot.js btw this isnt hosted (will host soon) or anything and to use it you have to use the cmd prefix (sc-) e.g., /sz-ping
Devlog 2 - server setup & modular command system & more [03/07/2026] time logged: 10hr 59 min so since the last devlog, i’ve have worked on LOADS of things including behind the scene (this doesnt get tracked on hacaktime :( ), i dont even know if this will hit the char limit, but anyways, here are the logs EDIT: IT DID HIT THE CHAR LIMIT, turns out the original devlog was ~9.6k characters 💀 while the limit is only 4k, so this is the shortened version. the full one is still in priv/devlogs/devlog2.md if anyone wants the extended edition.z Commits logs e47c48ac0970b49015e920607a59657259122f2f - made the cur proj into more modular featuring dynamic command loading system w commands.json as the source of truth on this, i figured out that packing every single thing into one file is a bad idea, so i made a modular command system that loads commands dynamically from a commands.json file, this way, i can add new commands without having to touch the main bot.js file, and also, it makes it easier to manage commands since they are all in one place 03b07ffc6f44d0231f9073f637caee1f9437705a) - added more detailed server logging with time & user at the start of most console logs onto terminal [coloured output] this was way more of a pain than i thought it was gonna be. after countless rounds of testing i finally got it working how i wanted. the ansi colours kept either leaking into the rest of the terminal or resetting back to the default colour when i needed them to go back to the current log level. i was genuinely losing my mind because every “fix” just broke something else. after hours of messing around i ended up making a wrapper so instead of resetting after things like the username or command, it switches back to whatever the current log level colour is (info, success or error), then only resets once right at the end of the log. it took way longer than it should’ve and nearly made me give up, but it finally does exactly what i wanted and nothing leaks anymore.added src/utils/logger.js and switched almost every console.log over to it. it adds timestamps, users, commands and coloured log levels.and changed all the console logs in src/bot.js and src/cmds/ to use the new logger api b81c739cc06edba2895695e51dbbf88d8868f19f - overhauled readme well as the name implies its pretty self explanatory lol but i massively expanded the README with better documentation and setup instructions. Server setup find more info in the setup guide on Step 7 out of 8 in https://stardance.hackclub.com/missions/slack-bot/guide#step-7 i had to apply for a Nest server and get SSH access.yeah so basically my starting gear is a potato server (2GB RAM, 16GB storage) but that’s more than enough for this bot. i also got to mess around with linux for a bit ( yes i am a windows user ). honestly… i kinda liked it and i’m like 80% convinced i’ll switch one day (but too much of a hassle to switch rn, but maybe in the future). after installing everything, setting up the environment variables and creating a systemd service, i finally got the bot running 24/7.TL;DR: i got the bot running 24/7 in the server and it works perfectly fine, and i can now add new commands without having to touch the main bot.js file, and also, and added detailed server logging with time & user aight y’all i spent an ENTIRE HOUR writing this devlog and THEN i had to rewrite a shortened down version, y’all better rate me 9/9 for story telling, rylvion signing out 🫡 pls see my full devlog version
Devlog 3 - github actions webhook request (13/07/2026) time logged: 6hr 01min i added a webhook dispatcher that automatically triggers a deployment whenever i commit to the main branch. Instead of manually pulling changes on the server and restarting the service myself, the GitHub Action now sends a signed webhook to my deployment server, which verifies it and runs the deploy script for me. i also added 2 more commands /sz-meme - this makes a meme /sz-echo [str] - this echoes the string you can see the flowchart below and see the full devlog 3 here (hyperlinked) TL;DR whenever i commit the server automatically receives it and slackzilla updates to the latest version
Devlog 4: calc and roll commands time logged: 2 hr 20 minutes date: 17/07/2026 today i spent some time reading the Java Shunting Yard Algorithm implementation on GeeksforGeeks . The example focuses entirely on converting an infix expression into postfix form. It scans the expression character by character, places operands directly into the output string, and uses a stack to manage operators based on precedence and associativity. Once the scan is complete, any remaining operators are appended to produce a postfix expression like 5238-52^^/+. The Java version supports operators including + , - , * , / , and ^ , and it handles parentheses, but it stops after producing the postfix representation. It does not evaluate the numerical result. after reading that, i wanted something more complete for Slackzilla. I used the same core idea of converting infix to postfix, but I extended it in several ways. My implementation tokenises the input instead of reading one character at a time, which allows it to support multi digit numbers and decimals. I added unary minus detection so expressions like -3 or 5*-2 behave correctly. One challenge was distinguishing unary minus from subtraction. The parser has to determine whether it starts with a number or subtracts two values depending on the previous token. I kept exponentiation with the correct right associativity. Once the postfix array is built, I added a second stage that evaluates the postfix expression using a number stack. This lets the function return the final computed value rather than just the postfix notation. The result is a more flexible and practical version of the algorithm that fits naturally into Slackzilla and can evaluate real expressions interactively. after finishing the calculator command, I also added another command to Slackzilla called /sz-roll . This command allows users to simulate dice rolls directly inside Slack. It supports normal dice notation like d6 , custom dice sizes like d20 , multiple dice such as 2d6 , and modifiers like 2d20+5 . I added parsing logic to split the dice expression into the amount of dice, number of sides, and any modifier before rolling each dice individually and calculating the final total. I also added a /sz-roll help and /sz-calc help command which explains how to use the command and shows examples of supported formats. also i spent A LOT of time making the flowchart to describe this more than the implementation itself. I think it will be useful for future reference and for anyone else who wants to understand how the algorithm works. i also added json schema validation for commands.json as talked in devlog 2 the commands.json is the source of truth, so i addded a validator.yml to validate the json schema of commands.json and make sure it is valid before a pull request is merged. This will help prevent errors and ensure that the commands are correctly defined. TL;DR: added a calculator using the Shunting Yard algorithm with postfix evaluation, plus a flexible dice rolling command. Also added help commands, a flowchart and automated JSON schema validation. also ill add way more devlogs from now on since most of my last devlogs were major architecture refactor, unless there is a major change, i will just add a small devlog to add a new command/small feature, fix a bug, improve an existing feature and add more documentation.
Devlog 5 - manifest workflow time logged: 7hrs 6min 16 sec date: 21/07/2026 ok so i kinda lied in the last devlog. this ended up being another major architectural change to slackzilla. the slack manifest system was always the most brittle part of the pipeline and manually maintaining manifest.json was a guaranteed way to let things drift. today i finally automated the entire manifest workflow. the new setup makes commands.json and meta.js the single source of truth. i wrote a manifest generator that loads all metadata like descriptions colours oauth scopes bot info and merges it with the command definitions to build a complete slack manifest object. once assembled it writes manifest.json and syncs it during deployment. validation is baked directly into the generator. i wired AJV into both stages. every command is validated against command.schema.json and the final manifest is validated again against slack’s official schema. if anything fails the generator stops immediately with a clear error instead of producing a broken file. this caught several issues especially after i moved all schemas into a dedicated schemas/ directory to clean up the project structure. the github actions workflow also got a major overhaul. every push now generates the manifest validates commands and manifest rotates the slack configuration token updates github secrets syncs the manifest through the slack api and deploys the bot. changing a slash command in commands.json now updates both the bot and the slack app config automatically. no more manual edits to manifest.json or the slack app settings. the entire pipeline is now self maintaining and fully automated. token rotation was the biggest pain. slack configuration tokens only last twelve hours so storing them in github secrets was not enough. i created a fine grained github PAT with permission to modify repo secrets and wrote rotateManifestToken.js to handle the full refresh cycle. it rotates the manifest token receives a new refresh token and updates both secrets so the next deployment does not break. i spent way too long debugging an invalid_refresh_token error. note that slack’s copy button does not copy the existing refresh token it generates a new pair. every time i clicked it the previously saved token became invalid instantly. once i realised that i started updating github secrets immediately after copying the new pair so everything stayed in sync. after that the rotated token still was not being passed correctly between github actions steps. i switched to GITHUB_OUTPUT so the rotation step could hand the new token to the manifest sync step. then slack started returning not_authed which led to another rabbit hole. the manifest update endpoint requires the token in the correct auth header not the request body. once i fixed the request format the entire pipeline finally ran clean end to end. See figure 2 for github actions workflow TL;DR slackzilla now auto generates its slack manifest validates everything with AJV rotates configuration tokens syncs the manifest during deployment and updates the bot server with minimal manual work. after a lot of debugging around token rotation schema mismatches and github actions quirks the deployment pipeline is basically self maintaining except refreshing the github PAT every ninety days.
Devlog 6 - command buffet time logged: 6hr 37m 19s date: 22/07/2026 okay since most of the major architectural refactors are done, i can finally focus on adding more commands to Slackzilla. this devlog is about the new commands that i added to the bot, which are mostly utility commands that can be used for various purposes. base64 encoding hashing - supports multiple algorithms, default: sha256 decoder - decodes every algorithm that hashing supports random ids (uuid) secure tokens random numbers unix date conversion text case conversion (camel, pascal, snake, kebab) character counter rock paper scissors magic 8ball (the only entertainment command added in this devlog) the trickier parts were the input parsers. hash has to decide whether the first word is an algorithm or part of the text, case needs to split words without mangling the content too much, and unix has to switch between timestamps and readable dates without getting confused by weird inputs. i also added guardrails for invalid ranges and missing text so the bot gives a useful reply instead of just silently failing. the fun commands were the easiest win. 8ball just picks from a response list, and rps does a quick match against the player choice and the bot choice to decide whether it was a win, lose, or tie. it is very low stakes code, which is honestly refreshing after the manifest workflow stuff from last time. all utilities commands all have a help command associated to it the current utilities commands include: /sz-base64 - encodes a string to base64 /sz-hash - generates a hash of a string, default: sha256 /sz-decode - decodes a base64 encoded string /sz-uuid - generates a random uuid /sz-token - generates a random secure token /sz-random - generates a random number in a given range /sz-unix - converts a unix timestamp to a human-readable date and vice versa /sz-case - converts a string to camelCase, PascalCase, snake_case, or kebab-case /sz-count - counts the number of characters in a string /sz-rps - plays rock paper scissors with the bot /sz-calc - evaluates mathematical expression so all of it has a 2nd argument for help, so for example /sz-hash help will show the usage and examples of the command. i also added on package.json , 2 new scripts "validate-commands" : "ajv validate -s schemas/command.schema.json -d src/data/commands.json" , "cmd-stats" : "node scripts/stats.js" one to validate the commands.json file against the command.schema.json file, and another to provide statistics about the number of commands in the commands.json file. this will help me keep track of how many commands are in the bot and also make sure that the commands.json file is valid before a pull request is merged. currently, the bot has 22 commands in total, and 11 of them are utility commands (10 of them got added into this devlog (excluding the /sz-8ball that also was added in this devlog, but is an entertainment command)). the other 11 commands are the ones that were already in the bot before this devlog. i need some feedback on what other commands i should add to the bot, so if you have any ideas, please let me know. (im running out of ideas * sighs * ) im also planning on adding a web dashboard to the bot, which will allow users to see the bot’s status, logs, and other information in a more user-friendly way. this will be a big change, so stay tuned for that in the next devlog TL;DR: added a bunch of new slash commands for Slackzilla, kept the implementations small and native where possible, and tightened up the input handling so the new tools fail a little more gracefully.
Devlog 7 - Adding a Web Dashboard (locally) time logged: 9hr 58m date: 31/07/2026 ███████╗██╗ █████╗ ██████╗██╗ ██╗███████╗██╗██╗ ██╗ █████╗ ██╔════╝██║ ██╔══██╗██╔════╝██║ ██╔╝╚══███╔╝██║██║ ██║ ██╔══██╗ ███████╗██║ ███████║██║ █████╔╝ ███╔╝ ██║██║ ██║ ███████║ ╚════██║██║ ██╔══██║██║ ██╔═██╗ ███╔╝ ██║██║ ██║ ██╔══██║ ███████║███████╗██║ ██║╚██████╗██║ ██╗███████╗██║███████╗███████╗██║ ██║ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ added a local prototype view of the web dashboard for slackzilla, its hosted on Github Pages and is a static site that is not connected to the bot yet, it shows the design and layout of the dashboard and how it will look when its connected to the bot. the dashboard is designed to be responsive and works on both desktop and mobile devices. it has a clean and modern design that is easy to navigate. for Github Pages hosting, i used gh-pages branch which meant creating a seperate workflow ( pages.yml ) to build and deploy the dashboard to the dist/ folder. before that i needed a way to make it minified and optimised for production, so i used terser , clean-css and html-minifier-terser to minify the js, css and html files respectively, i do realise that this meant adding extra dependencies to the project (even though its not linked to the bot yet). The deployment website will be hosted on the bot’s server ( rylvion.hackclub.app ) and on the same port as the webhook server i also added new logging functionality that return smth like this the one below, this helps me track the usage of the commands and see how many times they have been used, when they were last used and by whom. this will be useful for the dashboard to show command usage statistics and manage command permissions. { " commands " : { " sz-hash " : { " count " : 3 , " lastUsedAt " : " 2026-07-23T13:27:28.116Z " , " lastUser " : " rylvion " , " lastUserId " : " U0A84FA2NP2 " } } } TL;DR: added a local prototype view of the web dashboard for slackzilla, its hosted on Github Pages and is a static site that is not connected to the bot yet, it shows the design and layout of the dashboard and how it will look when its connected to the bot and logging functionality that return command usage statistics and manage command permissions. introduced a new logging system that logs to both console and a file. added command usage tracking to record how often each command is used. enhanced server.js to serve dashboard, logs, and status pages. improved webhook handling and added signature verification. added a new metrics utility and feedback.js for tracking command usage statistics. next devlog will be about connecting the dashboard to the bot & server and making it functional, its gonna be a long one so stay tuned for that
Devlog 8 - server hosted dashboard finally happened time logged: 17hr 47min 45s date: 16/08/2026 The server hosted dashboard is finally here! It is hosted with the webhook, connected to the bot, and shows logs, status, and other information. architecture The Slackzilla server-hosted dashboard is the web control centre sitting alongside the webhook (runs on the same port). The server runs the dashboard, API routes, authentication/session handling, CSRF protection, logging, monitoring and administrative controls. The dashboard has been moving from the earlier static local/pages prototype (see devlog 7) towards an EJS-based server-rendered architecture, with shared layouts/partials and a global dashboard styling system. The server also handles the GitHub deployment webhook on port 9000 , while Nginx sits in front of the application. The dashboard is intended to expose things such as bot status, logs, statistics, monitoring, API documentation and admin controls rather than sshing into the server and running commands manually I’ve been separating the dashboard’s presentation layer, API layer and server logic. During development, I encountered and fixed several contract problems where route modules expected helpers such as context.sendError() and context.sendOk() that the runtime context did not provide. I also changed the authentication-aware API/SSE routes so unauthenticated requests return proper JSON/HTTP responses rather than accidentally redirecting an EventSource connection into an HTML login page. The dashboard has admin-only functionality, CSRF protection and server-side authentication, whilst the bot itself uses Slack’s Events/Slash Command mechanisms for incoming Slack activity. Slack’s Events API delivers subscribed events to the HTTP endpoint, while slash commands are sent to the configured app endpoint via HTTP POST. /api/admin routes are protected by an authenticated admin session and CSRF protection. Admin authentication uses a session cookie, which is only created after logging in through rylvion.hackclub.app/admin/login . The admin password is never stored in plaintext. It is stored as a PBKDF2-HMAC-SHA512 hash in the server environment, using a random salt and a high iteration count. The dashboard does not allow the password to be created or changed, so the server environment must be updated manually before restarting the service. CSRF tokens are generated server-side and sent to the authenticated client via a cookie. For POST requests, the client must return the token in the X-CSRF-Token header, preventing unauthorised or forged requests from triggering admin actions. /api/ (excluding /api/admin ) has no authentication Port 9000 is used for the webhook and the dashboard whilst the bot itself runs on socket mode. The api that currently has these endpoints base url: https://rylvion.hackclub.app/api/ GET /api/status - returns the bot status and other information GET /api/logs - returns the bot logs from slackzilla.log as JSON, including the file size in bytes POST /api/admin/control - allow the admin to control the accepts several actions such as start , stop , restart , redeploy , refresh , refresh-logs , refresh-status (self explanatory actions) with dangerous actions (such as redeploy , restart and stop ) contains a confirmation prompt to prevent accidental execution of the action. GET /api/admin/feedback - returns database feedback as JSON GET /api/admin/feedback/:id - returns the feedback with the given ID as JSON POST /api/admin/feedback/:id - allows the admin to control and accepts 3 actions such as delete , read , unread , respond . GET /api/admin/events - is a SSE (Server Sent Event) endpoint that allows the admin to receive real time events from the server such as logs, status, feedbacks and other information about the bot.
Devlog 9: React Dashboard time logged: 29hr 1m date: 03/09/2026 The React dashboard is finally here! I completely migrated the old EJS dashboard with a new React app. THIS will be IS the biggest devlog yet, as it covers a lot of changes and new features. main features added is migration, codebase assistance, graphs, telemetary and a new api. Heres a summary of what i changed renamed src/ to bot/ - this is because its related to the bot and not the server and its not where all the main files are migrated the EJS dashboard into a React app overhauled the UI deleted the pages system and the workflow to build it added a new RAG modal that answers questions about the repo and shows related visual assets (also includes a new rag.js file that handles retrieval and generation of answers) The react dashboard has the following features: 10 pages (Home, Admin, Api, Commands, Docs, Home, Logs, AI, Sitemap, Status) 12 components 8 utilities js exports in the utils/ folder 404 page (with routing) & Error Boundary modal page public AI assistant page with repository-aware retrieval and related visual assets command API with stable IDs, command help, and captured command responses (so API can be called from the dashboard) public status, logs, metrics, command, and AI API endpoints admin feedback controls for filtering, viewing, replying, marking read/unread, and deleting (they dont work on windows only linux) CPU, memory, disk, and network telemetry with responsive graphs and hover data tooltips (so satisfying) Current React Architecture The active client source lives in server/client/src/ . Vite builds it into server/client/dist/ , which is served by server/server.js . React Router renders the page routes declared in server/client/src/components/utils/routes.js . The server still owns the HTTP status code: known browser routes receive the React shell with 200 , while unknown browser routes receive the same shell with 404 so the client can render the NotFound page without hiding the correct HTTP semantics. The old EJS dashboard and static page-generation flow are no longer the active dashboard path. The bot remains in bot/ , separate from the dashboard server, and the server uses explicit Node http handlers under server/api/ . RAG and Command APIs The new /ai page sends questions to POST /api/ask . server/lib/rag.js retrieves relevant repository text while excluding priv/ , .env files, dependencies, logs, and build output. It can identify related image assets such as the calculator flowchart without parsing binary image contents. If configured, the retrieved context is sent to the AI provider; otherwise the endpoint returns a local configuration message and source metadata. Commands are exposed through GET /api/commands , GET /api/commands/:id , and POST /api/commands/:id . The API adapter reuses the same command modules as Slack and captures their responses. The Commands page provides the interactive runner, while the AI page remains focused on repository questions. Explicit natural-language requests can still ask the RAG service to execute a recognised slash command and return its result. Limitations The migration exposed several boundaries that are now documented in docs/ : authentication and CSRF, the webhook protocol, API registration, setup, and RAG behaviour. Runtime persistence is still file-backed through server/database/store.js ; an external database remains a future adapter project rather than a required dependency. Linux systemd controls and deployment scripts are intended for the hosted environment. On Windows local development, the dashboard, APIs, telemetry, and React pages work, but system service actions and Bash deployment are not expected to succeed. The dashboard must be rebuilt and restarted after production client or server changes.
Devlog 10 - QOL DASHBOARD IMPROVEMENTS, RAG ENHANCEMENTS WITH TOOLS & CACING AND LOTS OF STUFF time logged: 27 hrs 30 mins date: 20/09/2026 version: v0.9.5-beta This devlog is COVERING A LOT of stuff that i did, but im not gonna drag this over like i did from all other devlogs so heres are the features: covering from v0.9.1-beta to v0.9.5-beta, see release-history.json for the full changelog Features added Added RAG TOOLS including getStatus - returns the status of the RAG getCommands - with enhanced filtering (filter by name, usage hint, id, category and fuzzy filter (makes it easier for RAG to find commands)) getReleaseInfo - returns the release info of the CURRENT version and (optionally) any other version (pass it by parameter) runCommand - runs a command by id getLogs - returns recent logs for the RAG to inspect (max 100, default 20) getMetrics - returns the metrics of the RAG (memory usage, cpu usage, etc) getRoutes - returns the routes of the APIs added 3 new scripts ( npm run ) which are rag - runs the RAG with or without the --ai flag to see context/answer cli rag:use-tool - runs a tool by name (with optional parameters) release - appends current release notes from package.json to release-history.json . Added new endpoints /api/telemetry - returns the telemetry history separately from the main status endpoint /api/command/:id - gets or executes a command by id /api/telemetry/stream - streams telemetry updates using SSE /api/admin/server-events - returns server events for the admin dashboard /api/ask - now uses SSE to stream the RAG’s activity ( yers now we can see the RAG in aciton ), context, tool usage, token usage and final response dashboard imporvements added a seperate admin server events page (wevent searching, filtering, prioritisation and stylying) added a command runner on the command page fixed recent activity not showing up on the dashboard Added visible AI activity to the AI page the RAG now emits activity events while processing a question AI page is cached on localStorage so you can go back and forth between pages without losing the AI page state Improved the RAG’s repository awareness added AST-based parsing using acorn and acorn-walk added persistent .rag/index.json added symbol search and dependency analysis added function, export, function call, environment variable and route extraction added richer code context and source metadata added automatic index rebuilding improved path-based dependency detection fixed the RAG detecting references created using path.join(rootDir, ...) add token budgeting and usage tracking. Changed how telemetry is handled telemetry history is no longer included in /api/status or any other endpoints CHANGED all admin endpoints so that none of them has the telemetryHistory anymore Added release history tracking added release-history.json /sz-release can now retrieve release information for different versions npm run release automatically appends the current release notes Tl;DR: RAG got a major upgrade with AST-powered repository indexing (acorn/acorn-walk), persistent indexing via .rag/index.json , symbol search, dependency analysis, function/export/call/env-var/route extraction, richer source metadata, automatic index rebuilding, 7 new RAG tools, 3 new CLI tools, SSE-powered streaming for /api/ask and telemetry, telemetry endpoints, admin server events, live AI activity tracing, token/context/tool usage visibility, AI session caching, release history tracking, telemetry separation from /api/status, admin API cleanup, and a large collection of bug fixes, repository-awareness improvements and major QOL improvements. . hopefully i will be devloging every version till i ship
devlog 11 - ADMIN EHANCEMENTS: errors views, log archiving & restoration, logs deletion controls, RAG IMPROVEMENTS AND MORE… date: 11/10/2026 time logged: 31hr 57m version: v0.9.6-beta this release focused on making Slackzilla easier to debug, manage and understand internally. a LOT of work went into the admin panel, route extraction and testing. 1. admin panel got some SERIOUS upgrades added 3 new admin pages for server events, errors and logs. also split admin.css into separate stylesheets because one giant stylesheet isn’t exactly a maintenance strategy. error monitoring added an error viewer with timestamps, stack traces and source files improved error messages to make them more user-friendly log management added a log archive viewer with backup timestamps, line counts and file sizes added backup restoration and switching added controls to delete specific lines, ranges or the last X lines added log rotation and telemetry reset controls added the /log-archives admin endpoint 7 new admin controls: delete-logs , delete-lines , delete-last , delete-line , rotate-logs , restore-log and reset-telemetry that’s a LOT more control over server logs than before. 2. endpoints & session security added 4 new endpoints : 3 admin endpoints: /errors , /errors/stream , /log-archives 1 public endpoint: /api/session /api/session now provides current session information, with csrfToken moved from /api/admin/summary to the new endpoint. also fixed the CSRF token not appearing in the document head. small bug, annoying consequences. removed the children=[] pattern from routes.js and its associated dependencies. 3. RAG / code parser improvements route extraction got a substantial upgrade. detects routes using exact path checks, prefix checks and array-based matching improved HTTP method inference for conditional routes, including GET and POST merges duplicate route entries and inferred methods expanded discovery beyond server/api/ , including /webhook and /admin/login improved /api/routes to return supported methods, match types, source files and line numbers the goal: give the RAG system more accurate route information instead of confidently retrieving incomplete or duplicated data. ᕦ(ò_óˇ)ᕤ 4. AI activity & markdown the AI activity view now displays elapsed time alongside tool summaries and responses, making it easier to follow what the system actually did. replaced the custom Markdown parser with marked AND dompurify for established parsing and HTML sanitisation. switched the configured model from openrouter/free to openai/gpt-6-luna , aiming for better performance and reliability. also deduplicated getReleaseInfo to avoid maintaining redundant version information. 5. testing added 3 npm scripts : test:code-parser for code parser tests test:rag for RAG tests test:all for the broader test suite and validation checks added automated code parser tests covering route extraction, HTTP method inference and deduplication. 6. docs updated README.md and project documentation to reflect the latest endpoints, admin controls, route extraction changes and RAG improvements. documentation staying in sync with the codebase. revolutionary concept, apparently. release summary v0.9.6-beta added: 4 endpoints 7 admin controls 3 admin pages 3 npm testing scripts improved error visibility, log management, session information and route extraction updated Markdown rendering, AI activity information and documentation the main focus was improving maintainability, debugging and the reliability of Slackzilla’s internal tooling. ( ͡° ͜ʖ ͡°) next devlog hint: a terminal is coming. think less clicking through admin pages, more interacting with Slackzilla directly through a terminal interface. details in the NEXT devlog. \(^o^)/...
|
1792752514.4039207
|
1793357314.4039207
|
|
40567
|
Complex CSH Tracker by @CoolDuck | Stardance
|
CoolDuck
|
1
|
151.24
|
1894
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-30T16:35:25Z", "hours": 140.0, "multiplier": 14.51, "payout": 1894, "blessing": "neutral", "text": "Complex CSH Tracker My school runs on a very old tracking system for CSH (Community Service Hours) meaning it was hard for us students to see how much we have as well as see what happens to our CSH requests. I made this to solve this problem and decided to join stardance for the fun of it :O Please beware of the bugs Tester passwords are in the readme Unfortunely drive picker functionality doesn’t work cuz i didnt verify my google app :(", "repo": "https://github.com/coolduckmaster/ComplexCSHTracker", "demo": "https://complex-csh-tracker.vercel.app/"}]...
|
[{"post_id": 79501, "ts": "202 [{"post_id": 79501, "ts": "2026-10-14T10:18:26Z", "hours": 11.2411, "text": "They changed the deadline 💔💔 Fixed some bugs relating to CSH registering. Fixed loading screen bug. Fixed Welcome back appearing after clicking log out. Additionaly filter for Approvals. Export to PDF finally works. Split the Approval into different files."}, {"post_id": 75609, "ts": "2026-09-30T15:31:35Z", "hours": 11.1422, "text": "This is it vercel intergrated product ready to ship the time is currently 10:30 pm please let this ship go through :))))"}, {"post_id": 73643, "ts": "2026-09-25T15:56:06Z", "hours": 12.3236, "text": "And now, the end is near. Added first version of the history page with some filter edited backend intergration for said history small ui changes to Approval small ui changes to dashboard side bar. many other misc ui changes bug fixes (?)"}, {"post_id": 67461, "ts": "2026-09-13T15:57:54Z", "hours": 10.9419, "text": "GOOGLE DRIVE BACKEND INTERGRATION Title says it all Misc loading screens (so like it loads or smth) Bug fixes???? Some other random things (like placeholder images and stuff)."}, {"post_id": 60285, "ts": "2026-09-02T15:42:39Z", "hours": 10.4444, "text": "GOOGLE DRIVE INTERGRATION Yes google Oauth Yes upload section Yes multiple files (WITH FILE IMAGE) No teacher backend (yet) No personal infomation (i hope)"}, {"post_id": 52431, "ts": "2026-08-22T15:34:31Z", "hours": 13.3567, "text": "idk did smth? I did something i guess????? I dont rmb to much school work. btw the google frontend button is wip"}, {"post_id": 46093, "ts": "2026-08-13T01:50:16Z", "hours": 10.9692, "text": "10 hours in 2 days New teacher note tab for approval-deny New 404 page (easter egg hidden) Bugfixes (note: School has started, I do not have the free time to do manythings)"}, {"post_id": 44042, "ts": "2026-08-09T15:34:21Z", "hours": 5.4497, "text": "MongoServerError: E11000 duplicate key error collection: login.cshes index: userId_1 dup key: New functions have arrived! Approve and Deny have been fleshed out. Futhermore the description has a functioning backend to show it! Request pages has a new description box with a word limit! Dashboard has now show updates when your requests been approved or denied! Fixed backend bugs(?) Much more small UI changes!"}, {"post_id": 43358, "ts": "2026-08-08T15:49:56Z", "hours": 5.2531, "text": "[vite] Internal server error: Transform failed with 1 error: Added a new details screen for Approval page (missing approve-deny features). Frontend edits for pre-existing UI in “Approval” panel. Backend API edits for intergrating. Slept 4 hours."}, {"post_id": 42719, "ts": "2026-08-07T15:59:58Z", "hours": 7.2431, "text": "PENDING REQUEST! Pending request pages for all your pending request needs! (UI up to change). Authware updated to be more cool Intergrated Authware to the frontend (like, 60% intergrated) Burnt bugs (?) Much more!"}, {"post_id": 42022, "ts": "2026-08-06T15:51:51Z", "hours": 6.7397, "text": "REQUEST-FUNCTIONALITY The Request pages have gain functionality! All of the request now go to the “Pending Hours” and is automatically logged in the Backend Database! The Approval panel is still in the works however."}, {"post_id": 41375, "ts": "2026-08-05T15:30:06Z", "hours": 5.3767, "text": "Request Page in the work LACKS FUNCTIONALITY. Backend API fixes (?). Frontend Dashboard edits. Bugs? (Vouch doesn’t exist yet. still figuring out a image storage solution)"}, {"post_id": 40799, "ts": "2026-08-04T15:36:25Z", "hours": 7.1403, "text": "NAV MENU BUTTON! Frontend stuff for the “Request” pages Backend API for like request and stuff. Fixed MANY things(?)."}, {"post_id": 40167, "ts": "2026-08-03T15:55:16Z", "hours": 7.6592, "text": "7 and a half hours spend on doing what??? Bugs, loads, and loads, of bugs. Fixed code after spliting the dashboard side panel. Backend stuff. DASHBOARD LAYOUT (Spend a large amount of time styling)."}, {"post_id": 38979, "ts": "2026-08-01T16:26:58Z", "hours": 6.0889, "text": "OOGA BOOGA DASHBOARD IT TOOK ME 6 HOURS STRAIGHT TO GET THIS TO WORK, I HAD TO STEAL BORROW HACKATIME HTML CODE FOR THIS!"}, {"post_id": 38267, "ts": "2026-07-31T16:02:28Z", "hours": 5.05, "text": "What the hell is jsx?? Started work on the “Dashboard” screen. stole it from Hackatime dashboard Inspired by hack a time dashboard. Added a frontend router (?). Did some more stuff!"}, {"post_id": 37665, "ts": "2026-07-30T15:38:21Z", "hours": 4.1078, "text": "Log - 4: on boarding done (?), rename the project to “Complex CSH Tracker.”, started work on a dashboard. Beside from that, I might have to rework some of the log in UI with what I have in mind."}, {"post_id": 37072, "ts": "2026-07-29T15:34:59Z", "hours": 5.625, "text": "Added On boarding logic so users can pick their class and stuff. (this took longer then expected)"}, {"post_id": 36407, "ts": "2026-07-28T14:40:37Z", "hours": 3.25, "text": "ጮልጋቹ ነዐጮቹ ዪልክጋዐጮ “Login Page” የልፏቹ የረቹልነቹ ⶴቹረየ ጮቹ ጎ ፪ቹቹክ ልፕ ፕⶴጎነ ቻዐዪ ⶴዐ፱ዪነ"}, {"post_id": 35857, "ts": "2026-07-27T15:50:05Z", "hours": 1.5753, "text": "Started work on the full stack log in functionally of this tracker website (It’s no longer simple)."}]...
|
Title: Complex CSH Tracker by @CoolDuck | Stardanc Title: Complex CSH Tracker by @CoolDuck | Stardance
Devlogs:
Started work on the full stack log in functionally of this tracker website (It’s no longer simple).
ጮልጋቹ ነዐጮቹ ዪልክጋዐጮ “Login Page” የልፏቹ የረቹልነቹ ⶴቹረየ ጮቹ ጎ ፪ቹቹክ ልፕ ፕⶴጎነ ቻዐዪ ⶴዐ፱ዪነ
Added On boarding logic so users can pick their class and stuff. (this took longer then expected)
Log - 4: on boarding done (?), rename the project to “Complex CSH Tracker.”, started work on a dashboard. Beside from that, I might have to rework some of the log in UI with what I have in mind.
What the hell is jsx?? Started work on the “Dashboard” screen. stole it from Hackatime dashboard Inspired by hack a time dashboard. Added a frontend router (?). Did some more stuff!
OOGA BOOGA DASHBOARD IT TOOK ME 6 HOURS STRAIGHT TO GET THIS TO WORK, I HAD TO STEAL BORROW HACKATIME HTML CODE FOR THIS!
7 and a half hours spend on doing what??? Bugs, loads, and loads, of bugs. Fixed code after spliting the dashboard side panel. Backend stuff. DASHBOARD LAYOUT (Spend a large amount of time styling).
NAV MENU BUTTON! Frontend stuff for the “Request” pages Backend API for like request and stuff. Fixed MANY things(?).
Request Page in the work LACKS FUNCTIONALITY. Backend API fixes (?). Frontend Dashboard edits. Bugs? (Vouch doesn’t exist yet. still figuring out a image storage solution)
REQUEST-FUNCTIONALITY The Request pages have gain functionality! All of the request now go to the “Pending Hours” and is automatically logged in the Backend Database! The Approval panel is still in the works however.
PENDING REQUEST! Pending request pages for all your pending request needs! (UI up to change). Authware updated to be more cool Intergrated Authware to the frontend (like, 60% intergrated) Burnt bugs (?) Much more!
[vite] Internal server error: Transform failed with 1 error: Added a new details screen for Approval page (missing approve-deny features). Frontend edits for pre-existing UI in “Approval” panel. Backend API edits for intergrating. Slept 4 hours.
MongoServerError: E11000 duplicate key error collection: login.cshes index: userId_1 dup key: New functions have arrived! Approve and Deny have been fleshed out. Futhermore the description has a functioning backend to show it! Request pages has a new description box with a word limit! Dashboard has now show updates when your requests been approved or denied! Fixed backend bugs(?) Much more small UI changes!
10 hours in 2 days New teacher note tab for approval-deny New 404 page (easter egg hidden) Bugfixes (note: School has started, I do not have the free time to do manythings)
idk did smth? I did something i guess????? I dont rmb to much school work. btw the google frontend button is wip
GOOGLE DRIVE INTERGRATION Yes google Oauth Yes upload section Yes multiple files (WITH FILE IMAGE) No teacher backend (yet) No personal infomation (i hope)
GOOGLE DRIVE BACKEND INTERGRATION Title says it all Misc loading screens (so like it loads or smth) Bug fixes???? Some other random things (like placeholder images and stuff).
And now, the end is near. Added first version of the history page with some filter edited backend intergration for said history small ui changes to Approval small ui changes to dashboard side bar. many other misc ui changes bug fixes (?)
This is it vercel intergrated product ready to ship the time is currently 10:30 pm please let this ship go through :))))
They changed the deadline 💔💔 Fixed some bugs relating to CSH registering. Fixed loading screen bug. Fixed Welcome back appearing after clicking log out. Additionaly filter for Approvals. Export to PDF finally works. Split the Approval into different files.
Ships:
Complex CSH Tracker My school runs on a very old tracking system for CSH (Community Service Hours) meaning it was hard for us students to see how much we have as well as see what happens to our CSH requests. I made this to solve this problem and decided to join stardance for the fun of it :O Please beware of the bugs Tester passwords are in the readme Unfortunely drive picker functionality doesn’t work cuz i didnt verify my google app :(...
|
1792853543.167628
|
1792939943.167628
|
|
901
|
Kern by @AntKru | Stardance
|
AntKru
|
3
|
153.98
|
2739
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-20T16:46:53Z", "hours": 65.0, "multiplier": 19.56, "payout": 1269, "blessing": "neutral", "text": "I have released the next version of my own kernel! The biggest feature I added is multitasking. While it doesn’t look that special, it was actually very difficult to make my kernel switch between different tasks! Here are some notes on how to try the project: If you don’t want/can’t run it, you can look at the video in the readme. If you want to run it on macOS with UTM, you have to force a PS/2 controller: https://docs.getutm.app/settings-qemu/qemu/#force-ps2-controller", "repo": "https://gitlab.com/AntKru/kern", "demo": "https://gitlab.com/AntKru/kern/-/releases/"}, {"ship_num": 2, "ts": "2026-07-24T06:30:43Z", "hours": 48.0, "multiplier": 19.83, "payout": 961, "blessing": "neutral", "text": "I added a CLI the kernel, which was possible, because I finally have a keyboard driver! It only took so long, because it was actually difficult to write it: I thought I have to program the PIC, but it was already disabled so I had to learn the more complicated APIC. There is a demo video in the README if you don’t want to run the kernel. If you want to run it on macOS with UTM, you have to force a PS/2 controller: https://docs.getutm.app/settings-qemu/qemu/#force-ps2-controller", "repo": "https://gitlab.com/AntKru/kern", "demo": "https://gitlab.com/AntKru/kern/-/releases/"}, {"ship_num": 3, "ts": "2026-06-20T14:34:14Z", "hours": 30.0, "multiplier": 17.21, "payout": 509, "blessing": "neutral", "text": "I made an x86_64 kernel using the limine boot protocol. I don’t know what the most difficult thing was, as even most basic things are very complicated for me as a beginner in osdev. I still have to learn much and the kernel will be improved, but I thing it kind of works now. I have a video demo in the readme, if you don’t have qemu . When building, remember to clone the submodules ( git submodule init && git submodule update ). You need xorriso to build the ISO. Sorry for mixing meson and make , but in the README is a guide. Please use clang, gcc is very complicated.", "repo": "https://gitlab.com/AntKru/kern", "demo": "https://gitlab.com/AntKru/kern/-/releases/"}]...
|
[{"post_id": 79283, "ts": "202 [{"post_id": 79283, "ts": "2026-10-13T09:56:47Z", "hours": 4.6628, "text": "Memory management problems… It seems like there was a critical bug sleeping inside my memory manager. It appeared since I started using the PMM in my AHCI driver, but I think the problem is in the virtual memory manager. For this reason I am working currently on the memory manager instead of the AHCI driver. Roadmap As soon as I fix the MM, I will continue on the AHCI driver. It probably won’t be finished until the end of stardance."}, {"post_id": 76791, "ts": "2026-10-04T16:08:54Z", "hours": 2.6242, "text": "AHCI! (SATA) I am jumping a little bit fast form PCI to AHCI, but I think the PCI driver is as good as I need it right now, and if I learn that it isn’t, I will fix it. Anyway, I am starting to work on AHCI! This is really cool, as this allows me to communicate to storage devices! At the moment the kernel just lists connected devices, but that’s just the beginning! Roadmap The AHCI driver is in a very early stage, and it will take me a lot of time to finish, as AHCI is relatively complicated and the documentation isn’t written for beginners like me ;-)"}, {"post_id": 74920, "ts": "2026-09-28T16:27:51Z", "hours": 3.6928, "text": "PCI! I am starting to write a PCI driver, which opens a new world to me! As you can see, the kernel is already able to print some information on boot about connected PCI devices. I also added a function that can search for a specific PCI device by it’s class, which will help me to implement drivers for specific PCI devices. Roadmap The real reason I am implementing PCI is that I need it to find the AHCI controller. The AHCI controller can be used to access connected storage devices, which is my long-term goal."}, {"post_id": 71311, "ts": "2026-09-20T16:38:40Z", "hours": 1.3486, "text": "Release 0.3.0 I finished the release, including the new borders command! The visible bugs that I found are also fixed, please report them if you find some! Roadmap The kernel isn’t finished yet, I will continue to improve it and see how far I will get."}, {"post_id": 70750, "ts": "2026-09-19T18:15:14Z", "hours": 4.075, "text": "Final improvements before the next release Status bar I moved the status bar from the bottom to the top and removed the borders, in my opinion it looks better this way. Framebuffer borders I am already working on a new command which will make it possible to see the borders of the framebuffer in QEMU. This will be useful for debugging while keeping the UI simple. Bugs There are still some obvious bugs: My printf() implementation seems to break with invalid format strings and I have a bug in my memory manager which prevents me from merging the command showing the screen borders into main. Roadmap I will try to fix the bugs, but the features are now complete. I want to release the new version tomorrow."}, {"post_id": 68765, "ts": "2026-09-15T18:23:31Z", "hours": 7.8061, "text": "printf compatability I improved the printf() compatability, so that I can pad numbers with or 0 . This is very useful, for example I now can use it to display time and date properly. Roadmap I don’t know yet what’s the next thing to do, maybe I will ship soon."}, {"post_id": 63678, "ts": "2026-09-07T16:41:58Z", "hours": 4.1103, "text": "Status bar! I added a status bar which show the current time. It is very basic, but makes the OS look way more friendlier in my opinion. Sorry for the weird messages in the main window, I am currently debugging my printf implementation. Roadmap I still have a problem with printing dates: The year 2006 would be printed as 206 , because the RTC has seperate registers for year and century, so if century is 20 and year is 6 it prints 206."}, {"post_id": 58325, "ts": "2026-08-30T18:21:09Z", "hours": 2.6081, "text": "“Windows” I am making it possible to split the framebuffer into multiple views. This should make it more easy to have a more advanced UI than a CLI already inside the kernel. Hopefully there will be user processes at some point, but I want to have a nice looking OS before that. Roadmap I still have to fix some things (as visible on the screenshot). After that, I can try to make use of it and add a status bar or a separate command window."}, {"post_id": 55470, "ts": "2026-08-27T10:27:14Z", "hours": 4.5317, "text": "RTC The kernel can now read the current time from the Computer’s RTC! At the moment, you can’t select the time zone, so the time is probably in UTC, depending on how the computer is configured. Roadmap I am planning to improve the use experience, for example by using multiple “windows”, to maybe even have a status bar."}, {"post_id": 52423, "ts": "2026-08-22T15:29:50Z", "hours": 4.2389, "text": "GCC support I added GCC support, which should (hopefully) make debugging easier for me. I can’t show it really on the screenshot, trust me, it is compiled with gcc! Roadmap I have still bugs in my multitasking system. Currently I get a page fault when compiling with optimization > -O2 with clang, the next step is to test it with GCC."}, {"post_id": 47914, "ts": "2026-08-16T05:50:20Z", "hours": 2.6892, "text": "Mutexes I implemented a simple mutex (which isn’t perfect yet, but it works). As visible on the screenshot, I used it to make it impossible for the test tasks to print at the same time. One problem with it is that it currently runs hlt after releasing a mutex so that a different task has a chance to acquire it, which doesn’t seem like the best solution to me. Roadmap I am making the terminal position atomic, so that I don’t need mutexes for output, which I can not use in interrupt handlers"}, {"post_id": 46418, "ts": "2026-08-13T16:41:17Z", "hours": 5.8897, "text": "Multitasking! I fixed the last bug (that I could find) and multitasking works now! On the screenshot you can see two tasks trying to print the numbers 0-19 at the same time. The CLI even waits until the tasks are finished! Road map The output is still a little bit broken, so I want to implement output buffering and mutexes, so that multiple threads can’t print at the same time."}, {"post_id": 44770, "ts": "2026-08-10T19:26:17Z", "hours": 6.2656, "text": "Multitasking… I am still working on multitasking, but I am stuck currently. I am trying to modify the stack frame so that asm_switch_task is called after iretq , but it doesn’t work. As you can see on the screenshot, I get a page fault as soon as it tries to switch tasks (in the debugging process I had it switch 2-3 times before it crashes, which is not really better) Road map My highest priority is to get task switching to work consistently. After that, I will see what I can polish."}, {"post_id": 43900, "ts": "2026-08-09T10:50:40Z", "hours": 2.4128, "text": "SSE register state switching! The kernel will now switch the SSE register state lazily, but I still have two problems: The Part for the registers in the TCB must be 16 byte aligned, which it currently not guaranteed The interrupt handlers are not allowed to use SSE instructions, but I have no way of verifying this Road map I think it is time to write a version of malloc() which can guarantee a specified alignment. After that, I can try to actually switch tasks automatically with a simple scheduler! I will probably use the PIT interrupt to switch to the next task in a list."}, {"post_id": 42570, "ts": "2026-08-07T11:56:52Z", "hours": 4.5606, "text": "It probably doesn’t sound special, but it is as important as everything else: SSE support SSE is an extension of the CPU which has 16 128-bit registers, but is rarely used. This means, that saving all of these registers every time a task switch occurs would cost some performance. For this reason, the CPU has a bit that can be set to indicate that a task switch occurred. In most cases, nothing happens and the registers are kept in their original state. But if a task needs to use SSE instructions, the CPU generates an interrupt, which manually saves the SSE registers. This is what I am working on currently. Custom ISR wrapper All interrupt handlers use now my custom wrappers, which made possible that I can work on SSE support."}, {"post_id": 40940, "ts": "2026-08-04T19:52:16Z", "hours": 5.0347, "text": "Array optimization (Screenshot 1) I now actually optimized the dynamic array type, which means that it allocates memory in bigger chunks instead of every time an element is added / removed. In the process I added some debug logging, so typing is now very difficult if you don’t know how it works with log level LOG_DEBUG . Custom ISR wrapper (Screenshot 2) I finally decided to implement my own wrapper for ISRs instead of relying on __attribute__((interrupt)) . This should allow me to use floating point math with the FPU in the future. Also, I can now read more registers on interrupt (screenshot). Road map I am currently working on making all ISRs compatible with my wrapper. Then I will continue trying to get multitasking to work."}, {"post_id": 38897, "ts": "2026-08-01T14:03:43Z", "hours": 6.4275, "text": "Multitasking (Screenshot 1) I decided to try to implement multitasking! The kernel can now switch tasks, but only one time. This is the first step in direction of real multitasking, but I still have much to do. It is interesting to see, How I realize how bad my implementations I did earlier are, because I don’t fully understand them. But now I can improve them! Dynamic arrays (Screenshot 2) I made a quick implementation of dynamic arrays for the keyboard driver, but as I realized that I need them to store a list of tasks, I decided to implement one optimized implementation I can use everywhere. I have implemented a very simple version and use it in the keyboard driver, but it isn’t optimized yet. In fact, it’s even worse than the one I had earlier in the keyboard driver, because every modification now requires a reallocation. Road map I am going to optimize dynamic arrays, and then continue with multitasking."}, {"post_id": 36558, "ts": "2026-07-28T19:08:32Z", "hours": 2.8569, "text": "PIT I quickly wrote a simple PIT (Programmable Interval Timer) driver, so that I have a basic sleep function. It isn’t really useful at the moment and not thread safe, but as it was very simple I thought why not? APIC I realized that MADT I/O APIC overrides also override the flags of entries, so I am using these now unless it’s overridden by the user. Road map As I have now a simple keyboard driver, I want to work on something new. Some things I am considering: Multitasking Filesystems ELF loading PCI USB All of this topics are completely new to me, so I will pick the one that looks the most simple and probably fail anyway."}, {"post_id": 33535, "ts": "2026-07-23T15:58:35Z", "hours": 2.3419, "text": "I am shipping soon! Some little improvements in messages I improved some messages, so that the kernel looks more polished to new users. v0.2.0 I released a new version, which is what I am shipping soon (if nothing goes wrong). It includes an interactive CLI, which was made possible with the new keyboard driver. I improved the scrolling performance a lot, which I personally really enjoy! Finally, I fixed a bug when printing numbers, so the printed memory map now has (mostly) correct addresses."}, {"post_id": 32511, "ts": "2026-07-21T18:56:41Z", "hours": 3.1614, "text": "The kernel is now interactive! Demo CLI I added a demo CLI (you can see it on the screenshot) where you can exit properly, run tests or view the old demo! Keyboard driver I had to fix some bugs with the keyboard driver, but now I think it works okay enough that the CLI is stable (previously, it kept parts from the old buffer when reading it again) Road map I want to add more features to the CLI, such as setting the log level. As you can see on the screenshot, the debug output disturbs the CLI."}, {"post_id": 31826, "ts": "2026-07-20T14:47:46Z", "hours": 5.0328, "text": "I FINALLY get interrupts from the keyboard! PS/2 Keyboard driver The problem was, that I mistook the IRQ number for the interrupt vector when calling my own function! Now I can start expanding my keyboard driver. Currently, it already prints the pressed keys to the screen, as you can see on the screen shot. Performance improvements when scrolling After I finished the interrupt stuff, I can focus on improving the TTY in other areas, like the performance. I am using now memset() and memcpy() functions when scrolling the frame buffer, which itself is not an improvement: I optimized the two functions to use copy 8-byte blocks ad uint64_t instead of each byte individually, which made a significant difference. Scrolling with KVM acceleration is now almost instant on my PC, and without it is as fast as it was before with acceleration. Road map I want to improve the TTY with things like buffering, which should improve the performance even more. When I have a solid TTY, I can rework the demo and ship again."}, {"post_id": 30816, "ts": "2026-07-18T19:00:05Z", "hours": 6.3653, "text": "I am still far away from getting interrupts from the keyboard, but I am still learning much and making progress on other parts of the kernel! PMM I added support for mapping pages manually, which means that I can program now the APICs! While debugging this, I also improved my Page Fault handler, which you can see on the screenshot. APIC driver The APIC driver should now be able to configure IRQs on the I/O APIC to get delivered to the local APIC, but I can’t test it as I still don’t know why I can’t get interrupts from the keyboard :-( Road Map I am still working on getting interrupts from the keyboard, which is my main goal before the next ship. I currently suspect that my PS/2 driver is broken."}, {"post_id": 29069, "ts": "2026-07-15T18:32:01Z", "hours": 7.5669, "text": "I prefer this devlog format, so I will stick to it. I/O APIC As I explained earlier, I need to configure the APIC to get interrupts. I think I only need the I/O APIC and not the local APIC, if I understand the limine documentation correctly. But when trying to access it’s registers, I got a page fault. It seems like I need to manually map the registers to virtual memory, which brings me to… Physical Memory Manager I need to do what I already feared when starting to write the PMM, manually editing page tables. I am working on a function which will map pages (Including the I/O APIC registers) to virtual addresses. For this, I need to walk the complete paging structure: PML4 (from cr3) -> PDPT -> PD -> PT I can already parse it until I have the PT’s address, but when accessing the PT I get a page fault, as you can see on the screenshot. Road map I just wanted to post this update so that the next devlog is not too long. I will continue to work on the PMM, until I can access the I/O APIC’s registers."}, {"post_id": 24628, "ts": "2026-07-08T10:52:11Z", "hours": 8.3475, "text": "I did not post for some time, so this devlog will be relatively long. PS/2 driver (Screenshot 1) I wanted to get the PIC to work, but as I had no idea why it didn’t work I thought that the PS/2 Controller might not be in a functioning state. So, I wrote a PS/2 driver which initializes the Controller, and also detects connected devices! PIC -> APIC After that, I finally found out that limine already sets up the APIC, which means that the PIC is no longer functional. So I decided that my next big goal is to learn how ACPI and APIC work… ACPI (Screenshot 2) As you can see on the screenshots, I started working on an ACPI driver, which is already able to find tables like the MADT, which I need to find the APICs UEFI This does not have anything to do with the progress of my project, but I learned how to use UEFI in QEMU, so that I can debug my ACPI driver better. Road map I will have to learn now how APICs work, so that I can get interrupts from the keyboard. Then, I can improve the keyboard driver, which should make the kernel way more fun to use!"}, {"post_id": 20746, "ts": "2026-07-02T13:20:11Z", "hours": 6.0672, "text": "I have finished implementing the memory manager! free() will now call pmm_free() to free unused pages. At the moment it does not make any difference, but if I want to extend the PMM it will have saved me the work. Also, I split the demo from the main function so that it is more maintainable. Log messages with the log level DEBUG will now only appear in debug builds. This made the output much more readable, as you can see in the screenshots."}, {"post_id": 19208, "ts": "2026-06-30T14:00:38Z", "hours": 9.6128, "text": "I have added a simple memory manager to my kernel! It was only possible with the help from kmos , a project I found while voting. I didn’t know how to start writing a physical memory manager, but in that kernel I found that I have to use the limine HHDM request to have simple access to physical addresses. Then I could implement the rest completely myself. The last thing left to do until I’m satisfied with my memory manager, is to free pages in the PMM after using free()"}, {"post_id": 12539, "ts": "2026-06-20T14:12:50Z", "hours": 0.8414, "text": "Now I am actually shipping! I had a bug that occured only when compiling with optimization level 2 or higher (which I used in the release CI). As I am working now on a Debian machine, I made it also compile on Debian stable by not requiring a new objcopy version. On the screenshot you can see the bug."}, {"post_id": 11482, "ts": "2026-06-18T19:17:02Z", "hours": 4.0292, "text": "I will ship the kernel now for the first time! I added a libk (like a libc but for the kernel) and implemented some cool functions like printf(), which I could use for cool error messages with hex numbers like the one on the screenshot."}, {"post_id": 9395, "ts": "2026-06-15T18:55:28Z", "hours": 2.4939, "text": "I sadly don’t have much to say, because I did make almost no progress. I am trying to use the PIC to get interrupts from the keyboard, but it doesn’t work for some reason. Maybe I will try to use APIC instead, but it’s more complicated."}, {"post_id": 8627, "ts": "2026-06-14T10:44:21Z", "hours": 2.2619, "text": "I finally figured out how I can integrate the external library! Now I can skip instructions when an interrupt is executed and continue execution"}, {"post_id": 7621, "ts": "2026-06-12T19:00:11Z", "hours": 1.0647, "text": "I just wanted to implement an interrupt handler specific to divide error, but it turned out to be way more difficult than I thought! To continue execution after the interrupt, I have to manually increment the instruction pointer manually. But to know by which amount, I need to now the size of the whole instruction. To find it I wanted to use a library called Zydis, but I could not get it to compile, because clang generates calls to memset() and memcpy() . As I can not link with the standard library, I will have to find a solution tomorrow."}, {"post_id": 5837, "ts": "2026-06-09T19:37:29Z", "hours": 3.1153, "text": "I added a demo to show the features of my kernel. The kernel is almost ready for the first ship!"}, {"post_id": 4373, "ts": "2026-06-07T18:00:51Z", "hours": 3.2469, "text": "After giving up on the memory manager, I added logging and very basic interrupt handling. I will ship the project when I have a demo of all the features and published ISOs."}, {"post_id": 3530, "ts": "2026-06-06T14:47:32Z", "hours": 2.2575, "text": "As I have now a very basic way to interact with the kernel, I will try to move on to more complex topics. At the moment I try to understand how memory management exactly works (specifically, how to write a physical memory manager), but it’s difficult to find information about it. Until now, I have requested a memory map from limine and can display it in a table."}, {"post_id": 3364, "ts": "2026-06-06T07:57:08Z", "hours": 1.7286, "text": "I implemented enter key press detection before doing anything else, so that I can interact somehow with the kernel. I did it with IO Ports instead of interrupts for now (because I don’t know how to set up interrupts), which means it only works with PS/2 Keyboards. Luckily, qemu shows the keyboard as a PS/2 Keyboard."}, {"post_id": 2308, "ts": "2026-06-04T18:38:28Z", "hours": 2.5331, "text": "I have added a normal font which makes things way simpler. I also improved the general tty: scrolling, new lines and tabs work now!"}, {"post_id": 1458, "ts": "2026-06-03T14:42:11Z", "hours": 1.7617, "text": "I spend so much time to fix a stupid bug (taking the address of a pointer instead of the address stored in the pointer), but know I can display characters from my ugly psf2 font! At least I learned more about gdb."}, {"post_id": 957, "ts": "2026-06-02T18:43:29Z", "hours": 3.0825, "text": "I implemented a set_pixel function, so that I can set specific pixels on the framebuffer. The next step will probably be to display text, maybe with a font made with psfedit, my project for flavortown. I also published my code on GitLab, which means I will try to build ISOs with gitlab CI at some point."}, {"post_id": 294, "ts": "2026-06-01T15:23:58Z", "hours": 1.1658, "text": "I have started to work on my kernel! I just set up my project structure but it does not work as you can see on the screenshot. I will look at it tomorrow. I have already tried to write a kernel earlier this week, but it was an i686 kernel with multiboot 2. Also, I had to copy much code from the limine template, but I will go beyond that when the kernel runs. This means, that I have a weird mix of makefile and meson, as the example uses make but I prefer meson. As I do not want to set up long mode myself, my new kernel uses limine instead of multiboot. My current plan is to get it to run and then publish the source code. Later, I might try to remove make completely and only rely on meson for building the iso."}]...
|
Title: Kern by @AntKru | Stardance
Devlogs:
I hav Title: Kern by @AntKru | Stardance
Devlogs:
I have started to work on my kernel! I just set up my project structure but it does not work as you can see on the screenshot. I will look at it tomorrow. I have already tried to write a kernel earlier this week, but it was an i686 kernel with multiboot 2. Also, I had to copy much code from the limine template, but I will go beyond that when the kernel runs. This means, that I have a weird mix of makefile and meson, as the example uses make but I prefer meson. As I do not want to set up long mode myself, my new kernel uses limine instead of multiboot. My current plan is to get it to run and then publish the source code. Later, I might try to remove make completely and only rely on meson for building the iso.
I implemented a set_pixel function, so that I can set specific pixels on the framebuffer. The next step will probably be to display text, maybe with a font made with psfedit, my project for flavortown. I also published my code on GitLab, which means I will try to build ISOs with gitlab CI at some point.
I spend so much time to fix a stupid bug (taking the address of a pointer instead of the address stored in the pointer), but know I can display characters from my ugly psf2 font! At least I learned more about gdb.
I have added a normal font which makes things way simpler. I also improved the general tty: scrolling, new lines and tabs work now!
I implemented enter key press detection before doing anything else, so that I can interact somehow with the kernel. I did it with IO Ports instead of interrupts for now (because I don’t know how to set up interrupts), which means it only works with PS/2 Keyboards. Luckily, qemu shows the keyboard as a PS/2 Keyboard.
As I have now a very basic way to interact with the kernel, I will try to move on to more complex topics. At the moment I try to understand how memory management exactly works (specifically, how to write a physical memory manager), but it’s difficult to find information about it. Until now, I have requested a memory map from limine and can display it in a table.
After giving up on the memory manager, I added logging and very basic interrupt handling. I will ship the project when I have a demo of all the features and published ISOs.
I added a demo to show the features of my kernel. The kernel is almost ready for the first ship!
I just wanted to implement an interrupt handler specific to divide error, but it turned out to be way more difficult than I thought! To continue execution after the interrupt, I have to manually increment the instruction pointer manually. But to know by which amount, I need to now the size of the whole instruction. To find it I wanted to use a library called Zydis, but I could not get it to compile, because clang generates calls to memset() and memcpy() . As I can not link with the standard library, I will have to find a solution tomorrow.
I finally figured out how I can integrate the external library! Now I can skip instructions when an interrupt is executed and continue execution
I sadly don’t have much to say, because I did make almost no progress. I am trying to use the PIC to get interrupts from the keyboard, but it doesn’t work for some reason. Maybe I will try to use APIC instead, but it’s more complicated.
I will ship the kernel now for the first time! I added a libk (like a libc but for the kernel) and implemented some cool functions like printf(), which I could use for cool error messages with hex numbers like the one on the screenshot.
Now I am actually shipping! I had a bug that occured only when compiling with optimization level 2 or higher (which I used in the release CI). As I am working now on a Debian machine, I made it also compile on Debian stable by not requiring a new objcopy version. On the screenshot you can see the bug.
I have added a simple memory manager to my kernel! It was only possible with the help from kmos , a project I found while voting. I didn’t know how to start writing a physical memory manager, but in that kernel I found that I have to use the limine HHDM request to have simple access to physical addresses. Then I could implement the rest completely myself. The last thing left to do until I’m satisfied with my memory manager, is to free pages in the PMM after using free()
I have finished implementing the memory manager! free() will now call pmm_free() to free unused pages. At the moment it does not make any difference, but if I want to extend the PMM it will have saved me the work. Also, I split the demo from the main function so that it is more maintainable. Log messages with the log level DEBUG will now only appear in debug builds. This made the output much more readable, as you can see in the screenshots.
I did not post for some time, so this devlog will be relatively long. PS/2 driver (Screenshot 1) I wanted to get the PIC to work, but as I had no idea why it didn’t work I thought that the PS/2 Controller might not be in a functioning state. So, I wrote a PS/2 driver which initializes the Controller, and also detects connected devices! PIC -> APIC After that, I finally found out that limine already sets up the APIC, which means that the PIC is no longer functional. So I decided that my next big goal is to learn how ACPI and APIC work… ACPI (Screenshot 2) As you can see on the screenshots, I started working on an ACPI driver, which is already able to find tables like the MADT, which I need to find the APICs UEFI This does not have anything to do with the progress of my project, but I learned how to use UEFI in QEMU, so that I can debug my ACPI driver better. Road map I will have to learn now how APICs work, so that I can get interrupts from the keyboard. Then, I can improve the keyboard driver, which should make the kernel way more fun to use!
I prefer this devlog format, so I will stick to it. I/O APIC As I explained earlier, I need to configure the APIC to get interrupts. I think I only need the I/O APIC and not the local APIC, if I understand the limine documentation correctly. But when trying to access it’s registers, I got a page fault. It seems like I need to manually map the registers to virtual memory, which brings me to… Physical Memory Manager I need to do what I already feared when starting to write the PMM, manually editing page tables. I am working on a function which will map pages (Including the I/O APIC registers) to virtual addresses. For this, I need to walk the complete paging structure: PML4 (from cr3) -> PDPT -> PD -> PT I can already parse it until I have the PT’s address, but when accessing the PT I get a page fault, as you can see on the screenshot. Road map I just wanted to post this update so that the next devlog is not too long. I will continue to work on the PMM, until I can access the I/O APIC’s registers.
I am still far away from getting interrupts from the keyboard, but I am still learning much and making progress on other parts of the kernel! PMM I added support for mapping pages manually, which means that I can program now the APICs! While debugging this, I also improved my Page Fault handler, which you can see on the screenshot. APIC driver The APIC driver should now be able to configure IRQs on the I/O APIC to get delivered to the local APIC, but I can’t test it as I still don’t know why I can’t get interrupts from the keyboard :-( Road Map I am still working on getting interrupts from the keyboard, which is my main goal before the next ship. I currently suspect that my PS/2 driver is broken.
I FINALLY get interrupts from the keyboard! PS/2 Keyboard driver The problem was, that I mistook the IRQ number for the interrupt vector when calling my own function! Now I can start expanding my keyboard driver. Currently, it already prints the pressed keys to the screen, as you can see on the screen shot. Performance improvements when scrolling After I finished the interrupt stuff, I can focus on improving the TTY in other areas, like the performance. I am using now memset() and memcpy() functions when scrolling the frame buffer, which itself is not an improvement: I optimized the two functions to use copy 8-byte blocks ad uint64_t instead of each byte individually, which made a significant difference. Scrolling with KVM acceleration is now almost instant on my PC, and without it is as fast as it was before with acceleration. Road map I want to improve the TTY with things like buffering, which should improve the performance even more. When I have a solid TTY, I can rework the demo and ship again.
The kernel is now interactive! Demo CLI I added a demo CLI (you can see it on the screenshot) where you can exit properly, run tests or view the old demo! Keyboard driver I had to fix some bugs with the keyboard driver, but now I think it works okay enough that the CLI is stable (previously, it kept parts from the old buffer when reading it again) Road map I want to add more features to the CLI, such as setting the log level. As you can see on the screenshot, the debug output disturbs the CLI.
I am shipping soon! Some little improvements in messages I improved some messages, so that the kernel looks more polished to new users. v0.2.0 I released a new version, which is what I am shipping soon (if nothing goes wrong). It includes an interactive CLI, which was made possible with the new keyboard driver. I improved the scrolling performance a lot, which I personally really enjoy! Finally, I fixed a bug when printing numbers, so the printed memory map now has (mostly) correct addresses.
PIT I quickly wrote a simple PIT (Programmable Interval Timer) driver, so that I have a basic sleep function. It isn’t really useful at the moment and not thread safe, but as it was very simple I thought why not? APIC I realized that MADT I/O APIC overrides also override the flags of entries, so I am using these now unless it’s overridden by the user. Road map As I have now a simple keyboard driver, I want to work on something new. Some things I am considering: Multitasking Filesystems ELF loading PCI USB All of this topics are completely new to me, so I will pick the one that looks the most simple and probably fail anyway.
Multitasking (Screenshot 1) I decided to try to implement multitasking! The kernel can now switch tasks, but only one time. This is the first step in direction of real multitasking, but I still have much to do. It is interesting to see, How I realize how bad my implementations I did earlier are, because I don’t fully understand them. But now I can improve them! Dynamic arrays (Screenshot 2) I made a quick implementation of dynamic arrays for the keyboard driver, but as I realized that I need them to store a list of tasks, I decided to implement one optimized implementation I can use everywhere. I have implemented a very simple version and use it in the keyboard driver, but it isn’t optimized yet. In fact, it’s even worse than the one I had earlier in the keyboard driver, because every modification now requires a reallocation. Road map I am going to optimize dynamic arrays, and then continue with multitasking.
Array optimization (Screenshot 1) I now actually optimized the dynamic array type, which means that it allocates memory in bigger chunks instead of every time an element is added / removed. In the process I added some debug logging, so typing is now very difficult if you don’t know how it works with log level LOG_DEBUG . Custom ISR wrapper (Screenshot 2) I finally decided to implement my own wrapper for ISRs instead of relying on __attribute__((interrupt)) . This should allow me to use floating point math with the FPU in the future. Also, I can now read more registers on interrupt (screenshot). Road map I am currently working on making all ISRs compatible with my wrapper. Then I will continue trying to get multitasking to work.
It probably doesn’t sound special, but it is as important as everything else: SSE support SSE is an extension of the CPU which has 16 128-bit registers, but is rarely used. This means, that saving all of these registers every time a task switch occurs would cost some performance. For this reason, the CPU has a bit that can be set to indicate that a task switch occurred. In most cases, nothing happens and the registers are kept in their original state. But if a task needs to use SSE instructions, the CPU generates an interrupt, which manually saves the SSE registers. This is what I am working on currently. Custom ISR wrapper All interrupt handlers use now my custom wrappers, which made possible that I can work on SSE support.
SSE register state switching! The kernel will now switch the SSE register state lazily, but I still have two problems: The Part for the registers in the TCB must be 16 byte aligned, which it currently not guaranteed The interrupt handlers are not allowed to use SSE instructions, but I have no way of verifying this Road map I think it is time to write a version of malloc() which can guarantee a specified alignment. After that, I can try to actually switch tasks automatically with a simple scheduler! I will probably use the PIT interrupt to switch to the next task in a list.
Multitasking… I am still working on multitasking, but I am stuck currently. I am trying to modify the stack frame so that asm_switch_task is called after iretq , but it doesn’t work. As you can see on the screenshot, I get a page fault as soon as it tries to switch tasks (in the debugging process I had it switch 2-3 times before it crashes, which is not really better) Road map My highest priority is to get task switching to work consistently. After that, I will see what I can polish.
Multitasking! I fixed the last bug (that I could find) and multitasking works now! On the screenshot you can see two tasks trying to print the numbers 0-19 at the same time. The CLI even waits until the tasks are finished! Road map The output is still a little bit broken, so I want to implement output buffering and mutexes, so that multiple threads can’t print at the same time.
Mutexes I implemented a simple mutex (which isn’t perfect yet, but it works). As visible on the screenshot, I used it to make it impossible for the test tasks to print at the same time. One problem with it is that it currently runs hlt after releasing a mutex so that a different task has a chance to acquire it, which doesn’t seem like the best solution to me. Roadmap I am making the terminal position atomic, so that I don’t need mutexes for output, which I can not use in interrupt handlers
GCC support I added GCC support, which should (hopefully) make debugging easier for me. I can’t show it really on the screenshot, trust me, it is compiled with gcc! Roadmap I have still bugs in my multitasking system. Currently I get a page fault when compiling with optimization > -O2 with clang, the next step is to test it with GCC.
RTC The kernel can now read the current time from the Computer’s RTC! At the moment, you can’t select the time zone, so the time is probably in UTC, depending on how the computer is configured. Roadmap I am planning to improve the use experience, for example by using multiple “windows”, to maybe even have a status bar.
“Windows” I am making it possible to split the framebuffer into multiple views. This should make it more easy to have a more advanced UI than a CLI already inside the kernel. Hopefully there will be user processes at some point, but I want to have a nice looking OS before that. Roadmap I still have to fix some things (as visible on the screenshot). After that, I can try to make use of it and add a status bar or a separate command window.
Status bar! I added a status bar which show the current time. It is very basic, but makes the OS look way more friendlier in my opinion. Sorry for the weird messages in the main window, I am currently debugging my printf implementation. Roadmap I still have a problem with printing dates: The year 2006 would be printed as 206 , because the RTC has seperate registers for year and century, so if century is 20 and year is 6 it prints 206.
printf compatability I improved the printf() compatability, so that I can pad numbers with or 0 . This is very useful, for example I now can use it to display time and date properly. Roadmap I don’t know yet what’s the next thing to do, maybe I will ship soon.
Final improvements before the next release Status bar I moved the status bar from the bottom to the top and removed the borders, in my opinion it looks better this way. Framebuffer borders I am already working on a new command which will make it possible to see the borders of the framebuffer in QEMU. This will be useful for debugging while keeping the UI simple. Bugs There are still some obvious bugs: My printf() implementation seems to break with invalid format strings and I have a bug in my memory manager which prevents me from merging the command showing the screen borders into main. Roadmap I will try to fix the bugs, but the features are now complete. I want to release the new version tomorrow.
Release 0.3.0 I finished the release, including the new borders command! The visible bugs that I found are also fixed, please report them if you find some! Roadmap The kernel isn’t finished yet, I will continue to improve it and see how far I will get.
PCI! I am starting to write a PCI driver, which opens a new world to me! As you can see, the kernel is already able to print some information on boot about connected PCI devices. I also added a function that can search for a specific PCI device by it’s class, which will help me to implement drivers for specific PCI devices. Roadmap The real reason I am implementing PCI is that I need it to find the AHCI controller. The AHCI controller can be used to access connected storage devices, which is my long-term goal.
AHCI! (SATA) I am jumping a little bit fast form PCI to AHCI, but I think the PCI driver is as good as I need it right now, and if I learn that it isn’t, I will fix it. Anyway, I am starting to work on AHCI! This is really cool, as this allows me to communicate to storage devices! At the moment the kernel just lists connected devices, but that’s just the beginning! Roadmap The AHCI driver is in a very early stage, and it will take me a lot of time to finish, as AHCI is relatively complicated and the documentation isn’t written for beginners like me ;-)
Memory management problems… It seems like there was a critical bug sleeping inside my memory manager. It appeared since I started using the PMM in my AHCI driver, but I think the problem is in the virtual memory manager. For this reason I am working currently on the memory manager instead of the AHCI driver. Roadmap As soon as I fix the MM, I will continue on the AHCI driver. It probably won’t be finished until the end of stardance.
Ships:
I made an x86_64 kernel using the limine boot protocol. I don’t know what the most difficult thing was, as even most basic things are very complicated for me as a beginner in osdev. I still have to learn much and the kernel will be improved, but I thing it kind of works now. I have a video demo in the readme, if you don’t have qemu . When building, remember to clone the submodules ( git submodule init && git submodule update ). You need xorriso to build the ISO. Sorry for mixing meson and make , but in the README is a guide. Please use clang, gcc is very complicated.
I added a CLI the kernel, which was possible, because I finally have a keyboard driver! It only took so long, because it was actually difficult to write it: I thought I have to program the PIC, but it was already disabled so I had to learn the more complicated APIC. There is a demo video in the README if you don’t want to run the kernel. If you want to run it on macOS with UTM, you have to force a PS/2 controller: https://docs.getutm.app/settings-qemu/qemu/#force-ps2-controller
I have released the next version of my own kernel! The biggest feature I added is multitasking. While it doesn’t look that special, it was actually very difficult to make my kernel switch between different tasks! Here are some notes on how to try the project: If you don’t want/can’t run it, you can look at the video in the readme. If you want to run it on macOS with UTM, you have to force a PS/2 controller: https://docs.getutm.app/settings-qemu/qemu/#force-ps2-controller...
|
1792842658.7836795
|
1792929058.7836795
|
|
3522
|
Inscryption Multiplayer Online by @MouthlessStoat Inscryption Multiplayer Online by @MouthlessStoat | Stardance...
|
MouthlessStoat
|
0
|
154.81
|
0
|
[]
|
[{"post_id": 57409, "ts": "202 [{"post_id": 57409, "ts": "2026-08-29T15:36:57Z", "hours": 9.0444, "text": "Devlog 14: Implemented a deck library feature to browse and edit all the deck. It was surprising hard to implement a way for deck from different format and ruleset to be converted to another ruleset. It almost look like a game, now only have to implement some small feature like variable attacks and missing sigils as well as custom sigils and card portraits."}, {"post_id": 55016, "ts": "2026-08-26T13:20:22Z", "hours": 18.4028, "text": "Devlog 13: Finishing and touching up the deck editor, now with actual filtering, saving decoration to distinguish different rarity. Along with this actually parse the side deck option and now they can be selected in the deck editor. Now working on a deck library function to display all the deck and allow them to be edit. Sigil progress is still going steadily"}, {"post_id": 47239, "ts": "2026-08-15T01:01:56Z", "hours": 10.8786, "text": "Devlog 12: Been spending the past few days implementing and fixing the card renderer, now everything should be final with no large major changes. All of this is for the first rudimentary version of the deck editor, None of the filtering or side deck works or even saving but I need to post a devlog and so this is it. After this deck editor tangent is done more sigil need to be implemented, luckily I do have a friend to help with implementing sigils."}, {"post_id": 44970, "ts": "2026-08-11T02:36:26Z", "hours": 7.2186, "text": "Devlog 11: Working on and perfecting active as well as stat buff sigil"}, {"post_id": 42038, "ts": "2026-08-06T16:23:29Z", "hours": 3.7253, "text": "Devlog 10: Finally something to show instead of just boring technical code. This is a new card renderer to get ready for the deck editor. Art are done by a friend of mine or ripped from vanilla."}, {"post_id": 40919, "ts": "2026-08-04T19:07:01Z", "hours": 7.09, "text": "Devlog 9: Implement rudimentary ruleset parsing so now custom card can be loaded externally instead of just hard coding it into the game. Still have much to do but this lay the foundation for the game."}, {"post_id": 37711, "ts": "2026-07-30T16:48:12Z", "hours": 8.6803, "text": "Devlog 8: Huge refactoring to clarify the shape of a bunch of type that have been just passed around as Dictionary until now. This should hopefully make further development easier as it make stuff more clear and concrete"}, {"post_id": 33276, "ts": "2026-07-23T02:05:40Z", "hours": 9.2731, "text": "Devlog 7: Another small batch of sigil. Various sigils that I can implement without large modification to the code base. Along with these implementation are small bug fixes here and there through out the codebase. Roughly 1/4 or 27 sigils out of the total 106 from vanilla base game have been implemented."}, {"post_id": 29969, "ts": "2026-07-17T05:50:52Z", "hours": 1.8678, "text": "Devlog 6: Spent way too long trying to solve lil bugs, this one have to do with a new AddCardAction specification. New instead of constructing and adding the card and expecting it to always be public, it now support private card, but need the user to make a new token if they want new card."}, {"post_id": 29888, "ts": "2026-07-17T01:36:21Z", "hours": 8.9164, "text": "Devlog 5: First small batch of sigil now that the stack system is mature, more to come over the next few devlog until most if not all sigils are implemented, then other features can be work upon."}, {"post_id": 28481, "ts": "2026-07-14T18:59:46Z", "hours": 33.1594, "text": "Devlog 4: Oops i went a long time without devlog, ourely because most of the work now are backend stuff that don’t really have anything to show for a devlog. Just boring design of the code base so that adding more feature in the future would be much much easier. Currently the action and stack system are being finalize, i am pretty confident this is the final revision with not much other changes. Sigil are still slowly being added with the increasing power of the stack system. No new major feature can be develop until the stack is perfect and capable. Cost and sigil that interact with them are finish, the custom cost system need to come later as I don’t want to spend even more time working on backend stuff and never releasing an alpha version."}, {"post_id": 9074, "ts": "2026-06-15T04:55:02Z", "hours": 14.1511, "text": "Devlog 3: Absolute countless night of head banging, finally implemented combat although a bit janky for now. But it work super well for Inscryption. . When the turn end, combat action is issues, which resolve into some amount of card attack action. There is a strike override for sigil like Bifurcated Strike or Trifurcated Strike, and a replacement ability hook now so stuff like air borne can be implemented. . Next devlog might be like a very very long time because there are so many sigil in base game Inscryption to implement but that need to be done. Ill check back in when most of the sigil category are implemented into the game and make sure they work well with each other. . After that are active sigil, proper fight handling, like the rest of the game (deck builder, ui polish, etc.). Then after some QA and Alpha testing it can go into public Beta as version 0.0.1 (We use pride versioning around these part and also cus pride month <3)"}, {"post_id": 6989, "ts": "2026-06-11T18:48:18Z", "hours": 7.3147, "text": "Devlog 2?: This is mainly a backend devlog as a bunch of stuff are tweak and slightly modify so the DX can be better. Action are now a new base clas that new action can inherit this streamline the process of making new action and resolver instead of moving between like 3 different file to make a new action you only need to look and modify 2. . A new total rework of the stack system to be more clean and easy to work with. The game now pause correctly and resolve them in sequence activating more sigil as they go along. Along with this new stack and a bunch of design document and deliberation for the combat system that should be implemented in the next devlog. Because it is all backend stuff there are nothing to show for this devlog </3"}, {"post_id": 4698, "ts": "2026-06-08T03:51:38Z", "hours": 1.8117, "text": "Multiplayer Working After literally hours of playing around with syncing and networking and trying to make sure the 2 client can talk to each other, finally made a working small demo of playing card and resolving the stack correctly. Instead of the playing fast and loose nature of Inscryption I opted instead to use a stack like Magic the Gathering to handle sigil resolution and ability trigger. This causes like a lot more issue than originally expected. I wanted both client to work independently when resolving sigil but they still need private information like hand and deck for card like Corpse Maggot and Pelt Lice. So now we have a very janky system where after every stack action the game sit there and wait for the other client to boardcast any private trigger they have so both client can continue the work. The system now is quite robust and can take alot of funky stack stuff. Next is working on the combat and programming in a bunch of sigils."}, {"post_id": 4627, "ts": "2026-06-08T00:30:06Z", "hours": 13.2789, "text": "Getting all the basic boilerplate down like handing a fight and resolving the stack and some cool lil UI"}]...
|
Title: Inscryption Multiplayer Online by @Mouthles Title: Inscryption Multiplayer Online by @MouthlessStoat | Stardance
Devlogs:
Getting all the basic boilerplate down like handing a fight and resolving the stack and some cool lil UI
Multiplayer Working After literally hours of playing around with syncing and networking and trying to make sure the 2 client can talk to each other, finally made a working small demo of playing card and resolving the stack correctly. Instead of the playing fast and loose nature of Inscryption I opted instead to use a stack like Magic the Gathering to handle sigil resolution and ability trigger. This causes like a lot more issue than originally expected. I wanted both client to work independently when resolving sigil but they still need private information like hand and deck for card like Corpse Maggot and Pelt Lice. So now we have a very janky system where after every stack action the game sit there and wait for the other client to boardcast any private trigger they have so both client can continue the work. The system now is quite robust and can take alot of funky stack stuff. Next is working on the combat and programming in a bunch of sigils.
Devlog 2?: This is mainly a backend devlog as a bunch of stuff are tweak and slightly modify so the DX can be better. Action are now a new base clas that new action can inherit this streamline the process of making new action and resolver instead of moving between like 3 different file to make a new action you only need to look and modify 2. . A new total rework of the stack system to be more clean and easy to work with. The game now pause correctly and resolve them in sequence activating more sigil as they go along. Along with this new stack and a bunch of design document and deliberation for the combat system that should be implemented in the next devlog. Because it is all backend stuff there are nothing to show for this devlog </3
Devlog 3: Absolute countless night of head banging, finally implemented combat although a bit janky for now. But it work super well for Inscryption. . When the turn end, combat action is issues, which resolve into some amount of card attack action. There is a strike override for sigil like Bifurcated Strike or Trifurcated Strike, and a replacement ability hook now so stuff like air borne can be implemented. . Next devlog might be like a very very long time because there are so many sigil in base game Inscryption to implement but that need to be done. Ill check back in when most of the sigil category are implemented into the game and make sure they work well with each other. . After that are active sigil, proper fight handling, like the rest of the game (deck builder, ui polish, etc.). Then after some QA and Alpha testing it can go into public Beta as version 0.0.1 (We use pride versioning around these part and also cus pride month <3)
Devlog 4: Oops i went a long time without devlog, ourely because most of the work now are backend stuff that don’t really have anything to show for a devlog. Just boring design of the code base so that adding more feature in the future would be much much easier. Currently the action and stack system are being finalize, i am pretty confident this is the final revision with not much other changes. Sigil are still slowly being added with the increasing power of the stack system. No new major feature can be develop until the stack is perfect and capable. Cost and sigil that interact with them are finish, the custom cost system need to come later as I don’t want to spend even more time working on backend stuff and never releasing an alpha version.
Devlog 5: First small batch of sigil now that the stack system is mature, more to come over the next few devlog until most if not all sigils are implemented, then other features can be work upon.
Devlog 6: Spent way too long trying to solve lil bugs, this one have to do with a new AddCardAction specification. New instead of constructing and adding the card and expecting it to always be public, it now support private card, but need the user to make a new token if they want new card.
Devlog 7: Another small batch of sigil. Various sigils that I can implement without large modification to the code base. Along with these implementation are small bug fixes here and there through out the codebase. Roughly 1/4 or 27 sigils out of the total 106 from vanilla base game have been implemented.
Devlog 8: Huge refactoring to clarify the shape of a bunch of type that have been just passed around as Dictionary until now. This should hopefully make further development easier as it make stuff more clear and concrete
Devlog 9: Implement rudimentary ruleset parsing so now custom card can be loaded externally instead of just hard coding it into the game. Still have much to do but this lay the foundation for the game.
Devlog 10: Finally something to show instead of just boring technical code. This is a new card renderer to get ready for the deck editor. Art are done by a friend of mine or ripped from vanilla.
Devlog 11: Working on and perfecting active as well as stat buff sigil
Devlog 12: Been spending the past few days implementing and fixing the card renderer, now everything should be final with no large major changes. All of this is for the first rudimentary version of the deck editor, None of the filtering or side deck works or even saving but I need to post a devlog and so this is it. After this deck editor tangent is done more sigil need to be implemented, luckily I do have a friend to help with implementing sigils.
Devlog 13: Finishing and touching up the deck editor, now with actual filtering, saving decoration to distinguish different rarity. Along with this actually parse the side deck option and now they can be selected in the deck editor. Now working on a deck library function to display all the deck and allow them to be edit. Sigil progress is still going steadily
Devlog 14: Implemented a deck library feature to browse and edit all the deck. It was surprising hard to implement a way for deck from different format and ruleset to be converted to another ruleset. It almost look like a game, now only have to implement some small feature like variable attacks and missing sigils as well as custom sigils and card portraits....
|
1792594495.2896154
|
1793199295.2896154
|
|
10038
|
Prometheus by @rishithc | Stardance
|
rishithc
|
1
|
154.99
|
82
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-06-07T15:56:14Z", "hours": 17.0, "multiplier": 5.0, "payout": 82, "blessing": "neutral", "text": "I made an autonomous drone. The most challenging part was the soldering. The soldering was quite hard for me and there were times where i burnt my hand", "repo": "https://github.com/rishith-c/Prometheus", "demo": ""}]...
|
[{"post_id": 29001, "ts": "202 [{"post_id": 29001, "ts": "2026-07-15T17:26:17Z", "hours": 10.9361, "text": "Devlog #18: A GREAT way to start working on your project again 😩 I’ve taken a break these past few days, realizing that a vacation exists for a reason! Today, however, I didn’t want to fall behind schedule. Since I also want grants to use a premium carbon fiber frame and all I need is to finish my PCB, I spent the day working in KiCad. Truth be told, the best way to learn is by doing—I thought the same about CAD, and I feel the same about PCB design. I made the executive decision to make a double-sided PCB rather than adding more layers, as it will be much simpler. Anyway, here are my final mockups."}, {"post_id": 23548, "ts": "2026-07-06T16:03:13Z", "hours": 9.0567, "text": "Devlog #17: I tested a lot tdy as well as designed new components I submitted my project, as is, to the Outpost fund, and I got some feedback. Mostly everything was good except for some issues with funding an NVDIA devkit :))), but the biggest issue was that I couldn’t buy a flight controller. In an devlog before, I mentioned and shows a picture of me designing a custom pcb, but I have not worked on that since. So today I fired up Kikad and designed layer 2 of my FC (easier, layer one has a lot of components that I need to import). I also spent a good chunk of today refining my 3d model, I used Air Shaper and SimScale and I aerodynamic tested all of the designs, I also asked claude for some ideas of how to make my .stls better. @vrunchil7 is da goat frfr he taught me how and what kikad is."}, {"post_id": 21535, "ts": "2026-07-03T15:40:49Z", "hours": 9.8875, "text": "Devlog #16: I probably should have done this earlier :( yea so while on vacation I decided to rework the weight and thrust ratios that the motors were producing with 11x7 props. I found out that my drone will not be able to fly SMOOTHLY. So I decided too look into why, one of the big reasons were that it was heavy, and two it wasn’t aerodynamic. So today I tried to kill two birds with one stone, (I also worked on my ML for a good portion of the time, but I feel this is more important). I ran digital wind tunnel tests and kept redesigning and testing until I landed on what I have now. I plan to make this out of carbon fiber which will be expensive, but ig that’s part of the process."}, {"post_id": 19917, "ts": "2026-07-01T11:05:00Z", "hours": 4.57, "text": "Devlog #15: training my own model wasn’t the move yea so training my own model wasnt the best idea. after hours of clicking through my webscraper’s image results and marking them, i ran a benchmark myself by putting on youtube videos of wildfires from a sky perspective. it saw the smoke in the distance and gave that as the fire, when the real fire was miles away. it also found the sun and clouds as a fire. so, false positives everywhere. so today i basically tested and worked with existing solutions instead, like this one: https://github.com/AlimTleuliyev/wildfire-detection , plus other open source alternatives on kaggle. but yea, overall today i felt retrogressed, and i’m thinking of going back to CV. This article also helped a lot https://www.mdpi.com/2571-6255/7/4/140 ."}, {"post_id": 19265, "ts": "2026-06-30T15:04:27Z", "hours": 9.0494, "text": "Devlog #14: building the dataset + training a tensorflow model spent today on the data side of fire detection, which is half the battle with ML. to train a model to recognize fire i need a solid labeled dataset, so first i built an image scraper. it crawls the web usinf breadth first search, going level by level through pages and pulling images as it goes. i used bidirectional bfs to expand the search from two fronts and cover more ground instead of just crawling one direction. ended up pulling a bunch of images, including a lot of tricky ones like trees and random orange stuff that a fire model could easily get confused by. then i built a small labeling tool to actually tag all of it. it shows me one image at a time, a tree, an orange object, an actual fire, and i just mark it as fire or no fire. way faster than sorting through folders by hand, and getting those confusing orange non-fire images labeled is exactly what stops the model from false triggering later. with some data labeled i started training a tensorflow model on it, mostly experimenting at this point to see how it behaves and what works. early days, but it’s a real step toward a model that actually learns what fire looks like instead of just matching colors. good progress on the part of this project."}, {"post_id": 18514, "ts": "2026-06-29T18:09:54Z", "hours": 18.7639, "text": "Devlog #13 : built a fire detection system for my drone’s Pi i genuinely lost my sanity working on ts (Everything is on gh) started on the payload for my autonomous quad: real time fire detection running on the Raspberry Pi. went classical CV with OpenCV instead of training a model. Since it has to run fully offline on the drone (no internet up there) so a heavy ML model was out. turns out you get pretty far with just color + motion. how it works: grabs the warm/bright pixels (orange/red/yellow in HSV, plus an R≥G≥B rule so it’s actually fire toned, not just bright) flicker analysis over a few frames. ts is the part that matters. real flame shimmers frame to frame, a static orange thing (a ball, a jacket, a sunset) doesn’t, so it won’t false trigger on every orange object it sees debounces over several frames so one noisy frame can’t trip it spits out a 0-100% confidence from area + flicker + persistence the two bits that do the work. color mask first: # warm/bright flame colors warm = cv2 . inRange ( hsv , ( 0 , sat_min , val_min ), ( 35 , 255 , 255 )) warm |= cv2 . inRange ( hsv , ( 170 , sat_min , val_min ), ( 179 , 255 , 255 )) # fire skews R >= G >= B with a real red lead, not just any bright pixel b , g , r = cv2 . split ( bgr . astype ( np . int16 )) order = (( r >= g ) & ( g >= b ) & ( r > 150 ) & (( r - b ) > 28 )). astype ( np . uint8 ) * 255 warm = cv2 . bitwise_and ( warm , order ) then the flicker check. just how much the fire region changed since last frame: diff = cv2 . absdiff ( mask , prev_mask ) flicker = np . count_nonzero ( diff ) / max ( fire_area , 1 ) # static orange object -> ~0 real flame -> way higher built a web dashboard for it too (Flask). same code runs on my laptop off the webcam to test, then goes straight on the Pi headless and i open it from my laptop browser over wifi. console sits calm/cyan while scanning, goes full red + pulsing + beeping the sec it locks fire. got live sliders to tune sensitivity so i’m not editing code every time. tested 4k fireplace video, locks on clean. waved random orange stuff it’s not certified or anything. classical CV WILL false trigger in harsh light so next: wire the “fire detected” event to grab the GPS coord off the flight controller and trigger a loiter so the drone marks the spot. btw if anyone’s done fire/smoke detection on a Pi 4 lmk if you went CV or tflite, curious what fps you got. 🫡"}, {"post_id": 16925, "ts": "2026-06-27T14:18:40Z", "hours": 11.3347, "text": "Devlog #12 : scrapped my CV code, went full YOLO ok!! so i went on a learning spree today and a LOT happened. ngl one of those days where you start the morning thinking you know stuff and end it realizing you knew nothing. ##i was doing my CV wrong so my old fire detection was classical CV, just color matching + flicker. cool little hack and it technically worked, but tbh it was way too fragile. anything orange in bright light could set it off, and it’s is supposed to fly on a drone in the sun, so that’s not happening. so i did the painful thing and scrapped the whole lot. felt bad deleting code that “worked” but it was never gonna be reliable enough out in the real world. went down the YOLO rabbit hole spent basically the whole day researching YOLO (you only look once, the object detection one). instead of me hand coding what fire “looks like” with color rules, the model actually learns it from examples. way more robust to lighting, angles, all the randome stuff a drone is gonna run into. got a basic YOLO model up and running. its rough rn but its already a different league from the color matching. side quest: mediapipe body tracking also messed around with mediapipe today as a practice project, tracked my own body/pose in real time. wasnt strictly for the drone, just wanted to get comfortable with actual CV pipelines and see how the landmark tracking works under the hood. hardware: turned my ceiling into a drone im on vacation so no real flying, but i still wanted to test from a drone’s POV. so i straight up taped a webcam to my ceiling pointing straight down. now i can test the CV from that top down birds eye angle, which is way closer to what the drone actually sees than my laptop cam pointed at my face. next up train the YOLO model on a proper fire dataset run it against the ceiling cam footage check fps on my laptop vs what i’ll realistically get on the Pi 🫡"}, {"post_id": 16627, "ts": "2026-06-27T01:24:09Z", "hours": 16.1558, "text": "Devlog #11: started the new CV code, deciding on the approach got through about half the CV code today, good progress on the rewrite. right now i’m deciding between two routes for the actual fire recognition: tensorflow + mobilenet-SSD — a proper deep learning object detector. heavier, but it actually learns what fire looks like, so it should handle different lighting and angles way better. regular opencv + a few libraries — lighter and simpler, runs easy on the Pi, but less robust. still weighing it. mobilenet-SSD is probably the stronger long term call since it generalizes better, but opencv is much lighter on the Pi, so i want to see what framerate i actually get before committing to either. next: benchmark both and pick one."}, {"post_id": 12308, "ts": "2026-06-20T02:48:00Z", "hours": 8.32, "text": "Devlog #10: motor test issues and a flight controller swap not gonna lie, things went downhill pretty fast today. i celebrated a bit early. all my motors spun fine on the individual motor test, so i figured i was basically there. then i hit “test all” and only 2 of the 4 motors spun. and it kept happening. i was sure it was a software/parameter thing at first, so i spent hours over the past few days going through ardupilot params trying to get all 4 to fire at once. couldn’t get it. every motor spins perfectly on its own, but only 2 ever spin on the test all. after sitting with it for a while, i’m pretty sure the issue comes down to the flight controller. i’d been running a cheap board that’s really built for fixed wing, and going through the ardupilot docs it just wasn’t the right fit for what i’m doing. so i’ve ordered a proper flight controller to swap in. the timing isn’t great. i leave for my trip the day after tomorrow, so for the next month i’m stuck on just the software side, no hardware. the new FC is on its way but realistically i can’t promise the drone actually flies until the end of july. bit of a setback, but at least i’ve got a direction now. onwards."}, {"post_id": 11790, "ts": "2026-06-19T05:52:36Z", "hours": 10.8169, "text": "Devlog #9: learning the AI side + designing a custom PCB really good, productive session today. spent the whole day learning. first half went into the machine learning side. since i want the algorithm to actually find and report fires properly, i went down a full rabbit hole on how AI works under the hood, neural networks, all of it. wanted to actually understand what’s going on instead of just copy pasting a model i don’t really get. second half i switched gears to hardware and started designing a PCB. right now my gps/compass, the SBC, and the ESC are all separate boards basically taped to a surface, which is messy and not great for an actual flying build. the plan is to get all of them onto one clean board instead. good mix of software and hardware today, feels like things are starting to come together."}, {"post_id": 11141, "ts": "2026-06-18T07:06:04Z", "hours": 4.8339, "text": "Devlog #8: it finally works oh my gosh. amazing and relieving day today. i’d been pretty down about this for a while. my motors just wouldn’t spin during the bench test, and i’d already ordered three ESCs, new motors, and new wires trying to fix it. none of it worked, so i returned all of it. today my new ESC finally showed up. i leave for india on sunday for a month, so these next few days are my only window to get any hardware done. so i sat down and meticulously (and painfully) soldered every joint, checking continuity with my multimeter over and over. a few connections literally fell apart while i was hooking up the wires, so i had to stop and resolder them. just wanted the wiring to be perfect this time. then the worst moment: i tested it and nothing worked. again. i was genuinely gutted. but then i noticed an orange cable just sitting there, not plugged in, and it clicked, that was the ground (GND). i connected the ESC ground to the FC ground, ran the motor test, and everything spun. all of it. no errors except some OSD stuff, which is just a missing SD card. massive relief. that one unplugged ground wire was the whole thing. next month in india i’ll be working on the code side of the drone, then test for flight again when i get back."}, {"post_id": 9775, "ts": "2026-06-16T06:54:09Z", "hours": 6.1247, "text": "Devlog #7: new ESC, somehow worse wow, rough day. got my new ESC in and resoldered every connection as cleanly as i could. but somehow it went the wrong way, instead of just one motor not working, now none of them work. so that’s fun. i’ve ordered another one. before i jump to blaming the flight controller this time, i want to go back through the wiring really meticulously and rule that out first. feels like it’s something small i keep missing. annoying timing too. i’m away from the 20th to the 20th, basically a month, so i’ll be forced to work on just the software side until i’m back. wish me luck pls."}, {"post_id": 7324, "ts": "2026-06-12T06:19:40Z", "hours": 4.2369, "text": "Devlog #6: down to a board problem today was a sad one. my new wires finally came and i swapped them all in, but the one motor still wouldn’t work. after that i’ve basically narrowed it down to a board problem. either i damaged the FC with my rough soldering, or it had some issue before i even started. so i’ve ordered a new board. i’m going on vacation soon, and i can only hope it arrives before i leave. if it doesn’t, i’ll be left to just improve, fine tune, and perfect the ML and software side until i’m back. not the day i wanted, but at least i think i know where the problem is now."}, {"post_id": 6395, "ts": "2026-06-10T19:14:57Z", "hours": 5.2294, "text": "Devlog #5: still chasing the one dead motor tested the drone with the battery again and the one motor still wouldn’t spin. while i was at it i calibrated most of the sensors, the accelerometer, GPS, and compass, so at least that side is sorted now. then i swapped the motor out again just to rule it out, and it still didn’t work. at this point i’m fairly sure it’s either an ESC problem or a wiring issue. my new wires come tomorrow, so i’ll be able to narrow it down properly and do what i can to fix it. slow progress, but i’m closing in on it."}, {"post_id": 6012, "ts": "2026-06-10T02:29:49Z", "hours": 8.6739, "text": "Devlog #4: chasing the dead motor + first autonomous code plugged in the battery and ran a test today, found a few bugs. the main one: one of the motors still wouldn’t work. so i went through it properly this time. resoldered all the connections, swapped in a known-working motor, still no spin. then i uncapped the battery pull going to that motor to check if it was a power issue, and still nothing. resoldered everything one more time, and it still wouldn’t spin. at that point i figured it has to be the bullet plug connectors, so i’ve ordered new ones. on the software side i actually made some progress, wrote some of the python autonomous flight code by SSHing into the raspberry pi. nice to move the code forward while the hardware’s still being stubborn."}, {"post_id": 4293, "ts": "2026-06-07T15:37:37Z", "hours": 9.2333, "text": "Devlog #3: designed and printed the entire drone chassis big hardware milestone today, i designed and 3d printed the entire drone chassis. did the whole design in onshape. spent a good while on the layout because the chassis has to hold everything: the flight controller, the ESC, the motor arms, the battery, the raspberry pi, and the gps/compass. i measured each component so i could put proper mounting holes and slots for all of them instead of just taping stuff to a plate. the motor arm spacing and overall geometry took the most back and forth, it has to be symmetric and square or the drone won’t fly right. M after a few tweaks to the model i sent it to the printer. next up is mounting everything onto it and starting the wiring."}, {"post_id": 3984, "ts": "2026-06-07T03:54:56Z", "hours": 6.7931, "text": "Designed and assembled the legs for my drone. I also soldered some electronic connections to the ESC (Electronic Speed Controller)."}, {"post_id": 3801, "ts": "2026-06-06T21:04:18Z", "hours": 0.8586, "text": "Devlog #1: designing and printing the arms kicking off a new project, an autonomous fire detecting and firefighting drone. the goal is a drone that can find and report fires on its own. starting with the hardware, so today i designed and printed the arms. did the design in onshape. the arms are carbon fiber tubes for the length and stiffness, paired with 3d printed plastic attachments that clamp onto the tubes and hold the motors out at the ends. carbon fiber keeps them light and rigid, which is exactly what you want for the arms, and the printed parts let me make mounts that actually fit my motors and bolt onto the frame. this session was mostly the build and assembly side, getting the carbon fiber pieces and the plastic attachments fitted together. feels good to have the first structural parts of the drone done. solid start, the skeleton’s starting to take shape."}]...
|
Title: Prometheus by @rishithc | Stardance
Devlog Title: Prometheus by @rishithc | Stardance
Devlogs:
Devlog #1: designing and printing the arms kicking off a new project, an autonomous fire detecting and firefighting drone. the goal is a drone that can find and report fires on its own. starting with the hardware, so today i designed and printed the arms. did the design in onshape. the arms are carbon fiber tubes for the length and stiffness, paired with 3d printed plastic attachments that clamp onto the tubes and hold the motors out at the ends. carbon fiber keeps them light and rigid, which is exactly what you want for the arms, and the printed parts let me make mounts that actually fit my motors and bolt onto the frame. this session was mostly the build and assembly side, getting the carbon fiber pieces and the plastic attachments fitted together. feels good to have the first structural parts of the drone done. solid start, the skeleton’s starting to take shape.
Designed and assembled the legs for my drone. I also soldered some electronic connections to the ESC (Electronic Speed Controller).
Devlog #3: designed and printed the entire drone chassis big hardware milestone today, i designed and 3d printed the entire drone chassis. did the whole design in onshape. spent a good while on the layout because the chassis has to hold everything: the flight controller, the ESC, the motor arms, the battery, the raspberry pi, and the gps/compass. i measured each component so i could put proper mounting holes and slots for all of them instead of just taping stuff to a plate. the motor arm spacing and overall geometry took the most back and forth, it has to be symmetric and square or the drone won’t fly right. M after a few tweaks to the model i sent it to the printer. next up is mounting everything onto it and starting the wiring.
Devlog #4: chasing the dead motor + first autonomous code plugged in the battery and ran a test today, found a few bugs. the main one: one of the motors still wouldn’t work. so i went through it properly this time. resoldered all the connections, swapped in a known-working motor, still no spin. then i uncapped the battery pull going to that motor to check if it was a power issue, and still nothing. resoldered everything one more time, and it still wouldn’t spin. at that point i figured it has to be the bullet plug connectors, so i’ve ordered new ones. on the software side i actually made some progress, wrote some of the python autonomous flight code by SSHing into the raspberry pi. nice to move the code forward while the hardware’s still being stubborn.
Devlog #5: still chasing the one dead motor tested the drone with the battery again and the one motor still wouldn’t spin. while i was at it i calibrated most of the sensors, the accelerometer, GPS, and compass, so at least that side is sorted now. then i swapped the motor out again just to rule it out, and it still didn’t work. at this point i’m fairly sure it’s either an ESC problem or a wiring issue. my new wires come tomorrow, so i’ll be able to narrow it down properly and do what i can to fix it. slow progress, but i’m closing in on it.
Devlog #6: down to a board problem today was a sad one. my new wires finally came and i swapped them all in, but the one motor still wouldn’t work. after that i’ve basically narrowed it down to a board problem. either i damaged the FC with my rough soldering, or it had some issue before i even started. so i’ve ordered a new board. i’m going on vacation soon, and i can only hope it arrives before i leave. if it doesn’t, i’ll be left to just improve, fine tune, and perfect the ML and software side until i’m back. not the day i wanted, but at least i think i know where the problem is now.
Devlog #7: new ESC, somehow worse wow, rough day. got my new ESC in and resoldered every connection as cleanly as i could. but somehow it went the wrong way, instead of just one motor not working, now none of them work. so that’s fun. i’ve ordered another one. before i jump to blaming the flight controller this time, i want to go back through the wiring really meticulously and rule that out first. feels like it’s something small i keep missing. annoying timing too. i’m away from the 20th to the 20th, basically a month, so i’ll be forced to work on just the software side until i’m back. wish me luck pls.
Devlog #8: it finally works oh my gosh. amazing and relieving day today. i’d been pretty down about this for a while. my motors just wouldn’t spin during the bench test, and i’d already ordered three ESCs, new motors, and new wires trying to fix it. none of it worked, so i returned all of it. today my new ESC finally showed up. i leave for india on sunday for a month, so these next few days are my only window to get any hardware done. so i sat down and meticulously (and painfully) soldered every joint, checking continuity with my multimeter over and over. a few connections literally fell apart while i was hooking up the wires, so i had to stop and resolder them. just wanted the wiring to be perfect this time. then the worst moment: i tested it and nothing worked. again. i was genuinely gutted. but then i noticed an orange cable just sitting there, not plugged in, and it clicked, that was the ground (GND). i connected the ESC ground to the FC ground, ran the motor test, and everything spun. all of it. no errors except some OSD stuff, which is just a missing SD card. massive relief. that one unplugged ground wire was the whole thing. next month in india i’ll be working on the code side of the drone, then test for flight again when i get back.
Devlog #9: learning the AI side + designing a custom PCB really good, productive session today. spent the whole day learning. first half went into the machine learning side. since i want the algorithm to actually find and report fires properly, i went down a full rabbit hole on how AI works under the hood, neural networks, all of it. wanted to actually understand what’s going on instead of just copy pasting a model i don’t really get. second half i switched gears to hardware and started designing a PCB. right now my gps/compass, the SBC, and the ESC are all separate boards basically taped to a surface, which is messy and not great for an actual flying build. the plan is to get all of them onto one clean board instead. good mix of software and hardware today, feels like things are starting to come together.
Devlog #10: motor test issues and a flight controller swap not gonna lie, things went downhill pretty fast today. i celebrated a bit early. all my motors spun fine on the individual motor test, so i figured i was basically there. then i hit “test all” and only 2 of the 4 motors spun. and it kept happening. i was sure it was a software/parameter thing at first, so i spent hours over the past few days going through ardupilot params trying to get all 4 to fire at once. couldn’t get it. every motor spins perfectly on its own, but only 2 ever spin on the test all. after sitting with it for a while, i’m pretty sure the issue comes down to the flight controller. i’d been running a cheap board that’s really built for fixed wing, and going through the ardupilot docs it just wasn’t the right fit for what i’m doing. so i’ve ordered a proper flight controller to swap in. the timing isn’t great. i leave for my trip the day after tomorrow, so for the next month i’m stuck on just the software side, no hardware. the new FC is on its way but realistically i can’t promise the drone actually flies until the end of july. bit of a setback, but at least i’ve got a direction now. onwards.
Devlog #11: started the new CV code, deciding on the approach got through about half the CV code today, good progress on the rewrite. right now i’m deciding between two routes for the actual fire recognition: tensorflow + mobilenet-SSD — a proper deep learning object detector. heavier, but it actually learns what fire looks like, so it should handle different lighting and angles way better. regular opencv + a few libraries — lighter and simpler, runs easy on the Pi, but less robust. still weighing it. mobilenet-SSD is probably the stronger long term call since it generalizes better, but opencv is much lighter on the Pi, so i want to see what framerate i actually get before committing to either. next: benchmark both and pick one.
Devlog #12 : scrapped my CV code, went full YOLO ok!! so i went on a learning spree today and a LOT happened. ngl one of those days where you start the morning thinking you know stuff and end it realizing you knew nothing. ##i was doing my CV wrong so my old fire detection was classical CV, just color matching + flicker. cool little hack and it technically worked, but tbh it was way too fragile. anything orange in bright light could set it off, and it’s is supposed to fly on a drone in the sun, so that’s not happening. so i did the painful thing and scrapped the whole lot. felt bad deleting code that “worked” but it was never gonna be reliable enough out in the real world. went down the YOLO rabbit hole spent basically the whole day researching YOLO (you only look once, the object detection one). instead of me hand coding what fire “looks like” with color rules, the model actually learns it from examples. way more robust to lighting, angles, all the randome stuff a drone is gonna run into. got a basic YOLO model up and running. its rough rn but its already a different league from the color matching. side quest: mediapipe body tracking also messed around with mediapipe today as a practice project, tracked my own body/pose in real time. wasnt strictly for the drone, just wanted to get comfortable with actual CV pipelines and see how the landmark tracking works under the hood. hardware: turned my ceiling into a drone im on vacation so no real flying, but i still wanted to test from a drone’s POV. so i straight up taped a webcam to my ceiling pointing straight down. now i can test the CV from that top down birds eye angle, which is way closer to what the drone actually sees than my laptop cam pointed at my face. next up train the YOLO model on a proper fire dataset run it against the ceiling cam footage check fps on my laptop vs what i’ll realistically get on the Pi 🫡
Devlog #13 : built a fire detection system for my drone’s Pi i genuinely lost my sanity working on ts (Everything is on gh) started on the payload for my autonomous quad: real time fire detection running on the Raspberry Pi. went classical CV with OpenCV instead of training a model. Since it has to run fully offline on the drone (no internet up there) so a heavy ML model was out. turns out you get pretty far with just color + motion. how it works: grabs the warm/bright pixels (orange/red/yellow in HSV, plus an R≥G≥B rule so it’s actually fire toned, not just bright) flicker analysis over a few frames. ts is the part that matters. real flame shimmers frame to frame, a static orange thing (a ball, a jacket, a sunset) doesn’t, so it won’t false trigger on every orange object it sees debounces over several frames so one noisy frame can’t trip it spits out a 0-100% confidence from area + flicker + persistence the two bits that do the work. color mask first: # warm/bright flame colors warm = cv2 . inRange ( hsv , ( 0 , sat_min , val_min ), ( 35 , 255 , 255 )) warm |= cv2 . inRange ( hsv , ( 170 , sat_min , val_min ), ( 179 , 255 , 255 )) # fire skews R >= G >= B with a real red lead, not just any bright pixel b , g , r = cv2 . split ( bgr . astype ( np . int16 )) order = (( r >= g ) & ( g >= b ) & ( r > 150 ) & (( r - b ) > 28 )). astype ( np . uint8 ) * 255 warm = cv2 . bitwise_and ( warm , order ) then the flicker check. just how much the fire region changed since last frame: diff = cv2 . absdiff ( mask , prev_mask ) flicker = np . count_nonzero ( diff ) / max ( fire_area , 1 ) # static orange object -> ~0 real flame -> way higher built a web dashboard for it too (Flask). same code runs on my laptop off the webcam to test, then goes straight on the Pi headless and i open it from my laptop browser over wifi. console sits calm/cyan while scanning, goes full red + pulsing + beeping the sec it locks fire. got live sliders to tune sensitivity so i’m not editing code every time. tested 4k fireplace video, locks on clean. waved random orange stuff it’s not certified or anything. classical CV WILL false trigger in harsh light so next: wire the “fire detected” event to grab the GPS coord off the flight controller and trigger a loiter so the drone marks the spot. btw if anyone’s done fire/smoke detection on a Pi 4 lmk if you went CV or tflite, curious what fps you got. 🫡
Devlog #14: building the dataset + training a tensorflow model spent today on the data side of fire detection, which is half the battle with ML. to train a model to recognize fire i need a solid labeled dataset, so first i built an image scraper. it crawls the web usinf breadth first search, going level by level through pages and pulling images as it goes. i used bidirectional bfs to expand the search from two fronts and cover more ground instead of just crawling one direction. ended up pulling a bunch of images, including a lot of tricky ones like trees and random orange stuff that a fire model could easily get confused by. then i built a small labeling tool to actually tag all of it. it shows me one image at a time, a tree, an orange object, an actual fire, and i just mark it as fire or no fire. way faster than sorting through folders by hand, and getting those confusing orange non-fire images labeled is exactly what stops the model from false triggering later. with some data labeled i started training a tensorflow model on it, mostly experimenting at this point to see how it behaves and what works. early days, but it’s a real step toward a model that actually learns what fire looks like instead of just matching colors. good progress on the part of this project.
Devlog #15: training my own model wasn’t the move yea so training my own model wasnt the best idea. after hours of clicking through my webscraper’s image results and marking them, i ran a benchmark myself by putting on youtube videos of wildfires from a sky perspective. it saw the smoke in the distance and gave that as the fire, when the real fire was miles away. it also found the sun and clouds as a fire. so, false positives everywhere. so today i basically tested and worked with existing solutions instead, like this one: https://github.com/AlimTleuliyev/wildfire-detection , plus other open source alternatives on kaggle. but yea, overall today i felt retrogressed, and i’m thinking of going back to CV. This article also helped a lot https://www.mdpi.com/2571-6255/7/4/140 .
Devlog #16: I probably should have done this earlier :( yea so while on vacation I decided to rework the weight and thrust ratios that the motors were producing with 11x7 props. I found out that my drone will not be able to fly SMOOTHLY. So I decided too look into why, one of the big reasons were that it was heavy, and two it wasn’t aerodynamic. So today I tried to kill two birds with one stone, (I also worked on my ML for a good portion of the time, but I feel this is more important). I ran digital wind tunnel tests and kept redesigning and testing until I landed on what I have now. I plan to make this out of carbon fiber which will be expensive, but ig that’s part of the process.
Devlog #17: I tested a lot tdy as well as designed new components I submitted my project, as is, to the Outpost fund, and I got some feedback. Mostly everything was good except for some issues with funding an NVDIA devkit :))), but the biggest issue was that I couldn’t buy a flight controller. In an devlog before, I mentioned and shows a picture of me designing a custom pcb, but I have not worked on that since. So today I fired up Kikad and designed layer 2 of my FC (easier, layer one has a lot of components that I need to import). I also spent a good chunk of today refining my 3d model, I used Air Shaper and SimScale and I aerodynamic tested all of the designs, I also asked claude for some ideas of how to make my .stls better. @vrunchil7 is da goat frfr he taught me how and what kikad is.
Devlog #18: A GREAT way to start working on your project again 😩 I’ve taken a break these past few days, realizing that a vacation exists for a reason! Today, however, I didn’t want to fall behind schedule. Since I also want grants to use a premium carbon fiber frame and all I need is to finish my PCB, I spent the day working in KiCad. Truth be told, the best way to learn is by doing—I thought the same about CAD, and I feel the same about PCB design. I made the executive decision to make a double-sided PCB rather than adding more layers, as it will be much simpler. Anyway, here are my final mockups.
Ships:
I made an autonomous drone. The most challenging part was the soldering. The soldering was quite hard for me and there were times where i burnt my hand...
|
1792875212.5828192
|
1793739212.5828192
|
|
46498
|
Bugatti Destrier by @nejenabinupa | Stardance
|
nejenabinupa
|
1
|
155.0
|
0
|
[{"ship_num": 1, "ts": "2026-1 [{"ship_num": 1, "ts": "2026-10-13T06:24:33Z", "hours": 155.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "This is my fest car model I make from scratch and I make it in to a cool video to share with you guys, hope you enjoy it :). all the details are in the Readme.", "repo": "https://github.com/binupax6699/BUGATTI-DESTRIER-3D-MODEL-CINEMATIC", "demo": "https://youtu.be/0gfVzRgbG9k?si=vrqBCMsIrrrFTEiZ"}]...
|
[{"post_id": 76883, "ts": "202 [{"post_id": 76883, "ts": "2026-10-04T22:42:10Z", "hours": 3.3019, "text": "Modle and the video is all set now"}, {"post_id": 57506, "ts": "2026-08-29T17:53:22Z", "hours": 4.6228, "text": "In the modle the head lights didn’t look right so I fixed it and I finshed all of the animations now jest need to render it"}, {"post_id": 56434, "ts": "2026-08-28T06:21:57Z", "hours": 6.4069, "text": "I admitted sum cool shots and work on refining sum animations"}, {"post_id": 54990, "ts": "2026-08-26T12:45:25Z", "hours": 8.5239, "text": "I rig the car to animate and put in to the map and I’m starting to animating it"}, {"post_id": 54010, "ts": "2026-08-24T19:07:52Z", "hours": 10.4875, "text": "I heavily modified existing map custom textures, terrain/topology edits, foliage, mountains by me. to animate the car."}, {"post_id": 53277, "ts": "2026-08-23T18:46:47Z", "hours": 11.6706, "text": "Finish the interior room so long and so much effect, and need to adjust sum small theing around the body of the car"}, {"post_id": 51875, "ts": "2026-08-21T19:14:48Z", "hours": 3.8514, "text": "I lost around 10h off work because the house not recoded in the Stardance page, Add so much material normal maps and and good amount of stuff to make it the thing"}, {"post_id": 51637, "ts": "2026-08-21T12:42:44Z", "hours": 9.3028, "text": "Add texture/materials to the car and it look good and added normal maps to everything i think to give so much depth and detail"}, {"post_id": 50972, "ts": "2026-08-20T13:26:09Z", "hours": 24.4308, "text": "I close completing the interior and I make the rim design for the car,"}, {"post_id": 49163, "ts": "2026-08-17T20:02:34Z", "hours": 9.635, "text": "I work on the interior and som small sections of the car and I need to finalize the interior too and small details and things"}, {"post_id": 48516, "ts": "2026-08-16T22:18:10Z", "hours": 7.3842, "text": "Cut out sum sections and cut out the headlights and add things and i guess finally i make the engine bay caret this time, because i remake this engine bay around 4 time because i didn’t had much reference images to make it."}, {"post_id": 47928, "ts": "2026-08-16T06:11:42Z", "hours": 9.1111, "text": "Cut out sections and add things like the horse shoe grill and now remaking the engine bet with new reference images."}, {"post_id": 46936, "ts": "2026-08-14T14:32:31Z", "hours": 6.9164, "text": "Today I make the front part of the Bugatti and the it look so good, I need to model the saids and the cut out"}, {"post_id": 46523, "ts": "2026-08-13T20:12:03Z", "hours": 8.2425, "text": "I make the engine bed and the roof scoop and added the things from the things. And I also make the topology much more good"}, {"post_id": 45928, "ts": "2026-08-12T18:25:02Z", "hours": 10.4517, "text": "Make the said skirt and remake the body because the topography got bad."}, {"post_id": 45341, "ts": "2026-08-11T18:47:59Z", "hours": 9.6428, "text": "Make the rare diffuser, and also ajest the topology so much better body flow and to get details after the general shape come."}, {"post_id": 44782, "ts": "2026-08-10T19:53:31Z", "hours": 11.3414, "text": "I’m making the Bugatti Destriel from scratch and I want it be like the real thing, and I’m only did the back part of it and there is long way to go."}]...
|
Title: Bugatti Destrier by @nejenabinupa | Stardan Title: Bugatti Destrier by @nejenabinupa | Stardance
Devlogs:
I’m making the Bugatti Destriel from scratch and I want it be like the real thing, and I’m only did the back part of it and there is long way to go.
Make the rare diffuser, and also ajest the topology so much better body flow and to get details after the general shape come.
Make the said skirt and remake the body because the topography got bad.
I make the engine bed and the roof scoop and added the things from the things. And I also make the topology much more good
Today I make the front part of the Bugatti and the it look so good, I need to model the saids and the cut out
Cut out sections and add things like the horse shoe grill and now remaking the engine bet with new reference images.
Cut out sum sections and cut out the headlights and add things and i guess finally i make the engine bay caret this time, because i remake this engine bay around 4 time because i didn’t had much reference images to make it.
I work on the interior and som small sections of the car and I need to finalize the interior too and small details and things
I close completing the interior and I make the rim design for the car,
Add texture/materials to the car and it look good and added normal maps to everything i think to give so much depth and detail
I lost around 10h off work because the house not recoded in the Stardance page, Add so much material normal maps and and good amount of stuff to make it the thing
Finish the interior room so long and so much effect, and need to adjust sum small theing around the body of the car
I heavily modified existing map custom textures, terrain/topology edits, foliage, mountains by me. to animate the car.
I rig the car to animate and put in to the map and I’m starting to animating it
I admitted sum cool shots and work on refining sum animations
In the modle the head lights didn’t look right so I fixed it and I finshed all of the animations now jest need to render it
Modle and the video is all set now
Ships:
This is my fest car model I make from scratch and I make it in to a cool video to share with you guys, hope you enjoy it :). all the details are in the Readme....
|
1792666284.448898
|
1792752684.448898
|
|
43641
|
JJK Minecraft Mod by @qstorm | Stardance
|
qstorm
|
1
|
157.0
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-26T18:29:09Z", "hours": 157.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "FULL SHOWCASE HERE This is a Minecraft mod based on the power system from Jujitsu Kaisen. It contains a combo system where if you press a number of keys in the right order, the power will activate. as well as lots of room for expansion with other powers thanks to how it’s built. Currently there are 5 functional abilities: The innate technique which slows down entities around the player; the blue technique which attracts entities to a ball; the red technique which repels enemies away from the user; the purple technique which destroys everything in it’s path; and the domain which sends enemies to a dimension where they are unable to do anything At first I had just planned on using this project as a blank slate to learn how to mod Minecraft, but eventually I got inspired to make more interesting things, and it led to me making one of my personal favorite fictional power systems, the JJK cursed techniques. It took a while to figure things out, I spend many hours looking through Minecraft source code and other peoples code as well as tutorials but eventually I figured out how to do different things. This was easily the most challenging part of the project, compared to a mathematically intensive project like openGL. I’m proud of so many things with this project. For one it is the first fun project I made (both to code and to play). I also made very good code. After studying Minecraft code and making tons of mistakes, I eventually learned how to make code that was both expandable and readable. I believe that given another week, I would have been able to create 5 other abilities. If you want to try the project, make sure to look through the readMe and install the mod on the 1.21.11M branch. The README provides critical information about how to use and install the mod.", "repo": "https://github.com/qstorm1/PracticeMod/tree/1.21.11M", "demo": "https://modrinth.com/project/jjk-abilities/settings/versions"}]...
|
[{"post_id": 74103, "ts": "202 [{"post_id": 74103, "ts": "2026-09-26T17:59:56Z", "hours": 0.5408, "text": "Build and Read me update"}, {"post_id": 74084, "ts": "2026-09-26T17:10:20Z", "hours": 4.5536, "text": "JJK Mod in minecraft This is the final devlog here is a youtube link to the showcase: Showcase After finishing the backend, I got a lot of progress done! I recommend watching the showcase but I managed to easily get all the abilities to a semi functional state. Thanks to the hard work of previous me, this wasn’t a hard task and I had no major problems. One interesting thing I did was how I worked with red. I used dot products to make red more usable, it actually pushes enemies back like in the show if they are in the same area as the direction red is moving. Other then that the systems were pretty easy to make. A big reason it took so long to get here was because I had no idea how to mod. Unfortunately in order to get everything done today, I had to cut a few corners, meaning that the power renderer is still a particle system. I could go on and on about what i want to add though (that will be in the ship) so I’ll just end it here"}, {"post_id": 73911, "ts": "2026-09-26T09:41:04Z", "hours": 11.1103, "text": "Near completion! It’s weird getting a feeling of satisfaction when deleting the most important part of my code. Thats what I felt when deleting the static variables that stored my data. All of the internal systems are now working and my code is so much cleaner. Now entity data is saved successfully, if you die, leave the world or exit the game. In order to do this I had to learn how to use interfaces well. Turns out casting is a lot more useful then one might expect. You can cast interfaces into classes and vise versa and you can use interfaces with getter and setter methods to get data from classes. I always though interfaces were a useless thing that people use to make better looking code but they are helpful. Anyway, I faced a few major bugs throughout the transition into CCA, the main one being the fact that I had to put an entrypoint in the fabric.mod.json file to render entities. It took a long time and I only found it because of some random reddit thread I found. The main issue was that when I implemented an interface, it was supposed to save data for that class in all entities. Heres a tip, if something is wrong and you can’t find the issue, think about whats happening under the hood. For me, I assumed that the entity data would automatically be initialized but interfaces can’t do that. I’m probably explaining it wrong (I’m a little tired rn with coding and college apps). Anyway, theres a lot more I want to do and I wish that stardance had lasted another week so that I could show you all my cool Ideas. I’ll make the final devlog and ship my project later today. So hopefully it works well :D (I worked on a lot of other stuff in these 10 hours but none are finished enough to devlog about; images have a peak)"}, {"post_id": 72944, "ts": "2026-09-24T02:46:16Z", "hours": 11.0214, "text": "Finished Cardinal Component re-design. It was really weird to get it working because I needed to use an interface and cast variables which I used to avoid doing. Nothing new unfortunately, next update will go into more detail on how I did this! (with new system this /stats command works :D)"}, {"post_id": 71437, "ts": "2026-09-20T20:24:18Z", "hours": 9.7322, "text": "Sorry, this update will probably be really boring. I’ve been sick for the past 3ish days but have still been trying to code. Unfortunately, that means most of my code is pretty bad. The basic rundown of what’s been happening is a realization that player data was being handled really stupidly. I wasn’t even saving things to files meaning once the server shut down, all the information would be deleted. Now I thought at first I could pretty easily deal with this after finishing the mod so I ignored it, but then I started having issues that came from synchronization with client and server. To those of you who looked at my previous devlogs, you can see that I constantly rewrote a lot of my code. This is because sometimes the client and server use the same static variables while other times they don’t. As a result of these two things, I decided to use a library to handle it called Cardinal Components. For most of my coding projects I’ve always wanted to ignore libraries to the best of my abilities and make stuff myself. I believe it’s a good to learn how the internals of a system work but the problem with me was that I would always stay low level. As I learned more about minecraft development, I learned how important it is to have both high and low level skills in order to make bigger projects. There will probably be only 2 or 3 more devlogs before I ship so I’ll try to get as much cool stuff done before then as possible. Big things are coming!"}, {"post_id": 69112, "ts": "2026-09-16T12:12:50Z", "hours": 10.1767, "text": "Limitless Innate Upgrades [upgrades showcase] ( https://youtu.be/m7zWsrxv2mA ) uh the devlog link is broken lol (or im just bad idk). I could talk about the things that I worked on behind the scenes unrelated to the innate upgrades but that’s very boring and I talked about them before, the main thing that I fixed was the handling of non-Living Entities with the limitless innate. Basically I used mixins to edit the move code as well as added some things to the client code. I talked a lot about how this worked in a previous devlog, but it took a lot of reading and understanding code to learn how to fix it. Unfortunately my command blocks were broken so I couldn’t showcase the projectile innate system, but otherwise most of the info is in the video"}, {"post_id": 67057, "ts": "2026-09-13T03:11:16Z", "hours": 10.8842, "text": "MY CODE IS SO TRASH! I was working on fixing my innate technique for limitless. The bug in question was an issue where falling non-living entities would end up rubber-banding before sometimes hitting the ground. (I’m not making a showcase video for this use the screenshot and ur imagination) The issue ended up being due to 2 things, one was that minecraft runs the move() function right after applyGravity() so my code would never get the chance to change the gravity. The second was the fact that move() calls are run both by the server and client. The first issue was relatively simple to fix, I just had to learn about advanced mixins and using @ModifyVariable correctly. However the second issue meant that I either had to send a ton of data with packets, or I could do what I had always wanted to do but knew it would take forever… code in client side compatibility. Anyway as of right now I am “almost” done with getting it working. I swear one of these days I am going to share something cool. probably next update or the one after, it’ll come out by Monday if I lock in."}, {"post_id": 65123, "ts": "2026-09-10T01:16:08Z", "hours": 9.3792, "text": "Blue Kinda Works! Video Showcase The showcase above includes some of the older versions, skip to 0:40 for official version (it was lagging at the time of recording though) The hardest part of this step was the value adjustments. Given my mod gives each player a random value for cursed energy and , a player needs to feel the diffrence between a weak sorcerer and a strong one. I’ll probably continue tweaking the settings for blue until I believe it is good enough. Anyway right now blue isn’t very complicated, it just detects all entities in a certain radius and gives them a velocity based on the gravitational force which is calculated based on radius and constants. I tried using the real life equations but had trouble with them. Finally, in case your wondering, I am planning on switching the particle renderer that is super laggy with a LWJGL openGL sphere renderer or maybe a block display but in order to do that I need to get good at rendering and make a client linear interpolate to simulate smooth movement and overall I really don’t want to but its gotta happen. Next update will either be a finished blue, or me whining about how trash my code is"}, {"post_id": 62746, "ts": "2026-09-06T08:09:20Z", "hours": 9.1467, "text": "I got the first power to work! you can find a showcase here: https://youtu.be/t6oah7P1Fyk it may not seem like much, but this means the lower level systems are working, and I can start focusing on making cool stuff. Other then that I also made the combo system more advanced by having it so u can run actions mid combo and you can detect how fast you did the combo. There are a few more things I want to add to the combo system and tbh I thought about re-coding it again (which is a pretty annoying habit that I’ve gotten #OCD) Next update if I can do it in under 10 hours is gonna be really cool and probably coming tomorrow, following might let u see it earlier :D"}, {"post_id": 61210, "ts": "2026-09-04T01:08:40Z", "hours": 6.9719, "text": "I finalized the system! The idea is that most of the boring stuff happens in semi-abstract classes like the Sorcery class and the Ability class, which handle server side management and universal methods. Meanwhile, I can initialize real logic through child classes such as the Limitless calss which extends sorcery or the Blue class which extends Ability. I copied the idea from minecrafts Entity system. I’ve also managed to get nearly all of the key binds to work including scrolling (I used a mixin for scrolling). A lot of the hours that were described in this update actually ended up in the previous devlog so in reality it took about 9ish hours of work. Anyway, mostly a programming update, but next time I’m going to start actually adding powers to my modpack!"}, {"post_id": 60678, "ts": "2026-09-03T06:09:23Z", "hours": 14.2872, "text": "The hud system is officially complete! I meant to post this devlog earlier but I had some problems :/ I spent a ton of time figuring out how to render images and I realized it was much easier to just make very specific math that draws lines to the HUD (It doesn’t help that you can’t draw curvy or diagnol lines) I fixed a ton of other problems as well that you can’t read in my commit history I think. This devlog was supposed to be posted a lot earlier but I ended up forgeting to post after writing it lmao. For that reason, I also have to say that the structure of the power system is done and now I’m working on bug fixes as well as making working powers. I really wish I could post a video showing you guys, I’ll try to do so next devlog using something less powerful then OBS. See ya"}, {"post_id": 56835, "ts": "2026-08-28T19:41:22Z", "hours": 10.6292, "text": "The main summery for today is mindless progress, I’ve continued to work on the Hud system for my combo system but faced multiple issues that took hours to fix (hence the lack of progress) for starters, somewhere during my modding jurney, I forgot that identifiers can’t have uppercase letters, leading me to try remaking the system multiple times. Additionally, while in my original system I added hud elements to the registry when updating, and removed hud elements when reseting, in the new system, I replace that with static variables and if loops. If I don’t face any more major bugs, the HUD system should be done by the next devlog!"}, {"post_id": 54766, "ts": "2026-08-25T21:25:00Z", "hours": 9.4258, "text": "I’ve been creating a HUD system, though it’s pretty boring so I get distracted and do other stuff. Anyway the combo system is fully functional. Through this I learned that static maps are completely unnecessary as static variables update in each client side jvm. I won’t get into the implementation of my HUD system or the other stuff I’m working on because they’re unfinished so this is more of a minor update"}, {"post_id": 52729, "ts": "2026-08-23T01:48:59Z", "hours": 9.7422, "text": "I made a combo system! right now it’s really buggy but what it does is similar to street fighter system, you can make a combo and then assign key’s to it. You need to press the key’s in the required number of time for the combo to work. there are a lot of problems with reset rn but the base system is there. Each player has a value called a “combo key” which have a timer that increase by one every tick and resets to 0 when the kye is pressed. these are values that are mapped to each player and only one instance of each key is allowed per player (for better latency and client2server stuff) A combo is created using a bunch of if logic. I used a builder sytem for object creation so instead of new object() you do Combo.build().key1(timeNeeded to press).key2… Each combo can be used for abilities that I’m going to work on next. Speaking about abilities I decided what my mod would be based on, Jujitsu Kaisen! While i’m not a diehard fan of the show, the power system is one of the coolest in all of fiction, so I thought it would be fun to try and implement some abilities (hence the pocket dimention my screenshots are in) anyway, gonna fix the combo system and then work on a simple power see ya!"}, {"post_id": 49994, "ts": "2026-08-19T00:51:50Z", "hours": 4.3547, "text": "Made Laser Eyes! When you click the laser button which you can change in the controls menu, a particle will appear (which is purely visual) and it will make a raycast, targeting the nearest entity/block. It will damage an entity and turn the block into a redstone block (temporary until i feel like making ash). It also works in multiplayer, where the particles are summoned from the eye of the shooter from everyones persepective except the shooter who only sees the beam if a few blocks away that way they don’t get blinded by particles (the pictures use free cam and i have no friends so I can’t show this but u can see how it works in the code) My project didn’t have documentation so it was difficult figuring out how to make a raycast (combination of .clip and .getEntityHitResult) and how to tell if the player really pressed the laser button which requires client to server packet management."}, {"post_id": 49285, "ts": "2026-08-18T00:21:25Z", "hours": 3.7019, "text": "the first devlog has a large number of hours on it because of some issues with git and project stuff (you didn’t miss any updates other then reading don’t worry), Anyway, I’m chilling in my new dimention with my new potion effect that I get after using a new item (with iron helmet texture cause i’m bad). I’ve been trying to get a raycasting system to work I just wanted to update the project so that my hours can be counted correctely, future update coming soon!"}, {"post_id": 44947, "ts": "2026-08-11T01:33:50Z", "hours": 21.76, "text": "I looked through minecrafts source code, The version of minecraft I am coding is 1.21.11 and on fabric there are two “mappings” or interpretations of minecrafts source code. the first version is the community one called yarn which was the one that I was planning on using when I was watching a tutorial series on Youtube however the documentation uses the other mappings called the “mojang mappings”. I switched to the mojang mappings and looked through minecrafts source code using that. I figured out how to handle terrain generation, add new things to minecrafts registry system, and access player data. My next goal is to look through some real applications of mods and this is how I recommend others who are interested to do so to!"}]...
|
Title: JJK Minecraft Mod by @qstorm | Stardance
D Title: JJK Minecraft Mod by @qstorm | Stardance
Devlogs:
I looked through minecrafts source code, The version of minecraft I am coding is 1.21.11 and on fabric there are two “mappings” or interpretations of minecrafts source code. the first version is the community one called yarn which was the one that I was planning on using when I was watching a tutorial series on Youtube however the documentation uses the other mappings called the “mojang mappings”. I switched to the mojang mappings and looked through minecrafts source code using that. I figured out how to handle terrain generation, add new things to minecrafts registry system, and access player data. My next goal is to look through some real applications of mods and this is how I recommend others who are interested to do so to!
the first devlog has a large number of hours on it because of some issues with git and project stuff (you didn’t miss any updates other then reading don’t worry), Anyway, I’m chilling in my new dimention with my new potion effect that I get after using a new item (with iron helmet texture cause i’m bad). I’ve been trying to get a raycasting system to work I just wanted to update the project so that my hours can be counted correctely, future update coming soon!
Made Laser Eyes! When you click the laser button which you can change in the controls menu, a particle will appear (which is purely visual) and it will make a raycast, targeting the nearest entity/block. It will damage an entity and turn the block into a redstone block (temporary until i feel like making ash). It also works in multiplayer, where the particles are summoned from the eye of the shooter from everyones persepective except the shooter who only sees the beam if a few blocks away that way they don’t get blinded by particles (the pictures use free cam and i have no friends so I can’t show this but u can see how it works in the code) My project didn’t have documentation so it was difficult figuring out how to make a raycast (combination of .clip and .getEntityHitResult) and how to tell if the player really pressed the laser button which requires client to server packet management.
I made a combo system! right now it’s really buggy but what it does is similar to street fighter system, you can make a combo and then assign key’s to it. You need to press the key’s in the required number of time for the combo to work. there are a lot of problems with reset rn but the base system is there. Each player has a value called a “combo key” which have a timer that increase by one every tick and resets to 0 when the kye is pressed. these are values that are mapped to each player and only one instance of each key is allowed per player (for better latency and client2server stuff) A combo is created using a bunch of if logic. I used a builder sytem for object creation so instead of new object() you do Combo.build().key1(timeNeeded to press).key2… Each combo can be used for abilities that I’m going to work on next. Speaking about abilities I decided what my mod would be based on, Jujitsu Kaisen! While i’m not a diehard fan of the show, the power system is one of the coolest in all of fiction, so I thought it would be fun to try and implement some abilities (hence the pocket dimention my screenshots are in) anyway, gonna fix the combo system and then work on a simple power see ya!
I’ve been creating a HUD system, though it’s pretty boring so I get distracted and do other stuff. Anyway the combo system is fully functional. Through this I learned that static maps are completely unnecessary as static variables update in each client side jvm. I won’t get into the implementation of my HUD system or the other stuff I’m working on because they’re unfinished so this is more of a minor update
The main summery for today is mindless progress, I’ve continued to work on the Hud system for my combo system but faced multiple issues that took hours to fix (hence the lack of progress) for starters, somewhere during my modding jurney, I forgot that identifiers can’t have uppercase letters, leading me to try remaking the system multiple times. Additionally, while in my original system I added hud elements to the registry when updating, and removed hud elements when reseting, in the new system, I replace that with static variables and if loops. If I don’t face any more major bugs, the HUD system should be done by the next devlog!
The hud system is officially complete! I meant to post this devlog earlier but I had some problems :/ I spent a ton of time figuring out how to render images and I realized it was much easier to just make very specific math that draws lines to the HUD (It doesn’t help that you can’t draw curvy or diagnol lines) I fixed a ton of other problems as well that you can’t read in my commit history I think. This devlog was supposed to be posted a lot earlier but I ended up forgeting to post after writing it lmao. For that reason, I also have to say that the structure of the power system is done and now I’m working on bug fixes as well as making working powers. I really wish I could post a video showing you guys, I’ll try to do so next devlog using something less powerful then OBS. See ya
I finalized the system! The idea is that most of the boring stuff happens in semi-abstract classes like the Sorcery class and the Ability class, which handle server side management and universal methods. Meanwhile, I can initialize real logic through child classes such as the Limitless calss which extends sorcery or the Blue class which extends Ability. I copied the idea from minecrafts Entity system. I’ve also managed to get nearly all of the key binds to work including scrolling (I used a mixin for scrolling). A lot of the hours that were described in this update actually ended up in the previous devlog so in reality it took about 9ish hours of work. Anyway, mostly a programming update, but next time I’m going to start actually adding powers to my modpack!
I got the first power to work! you can find a showcase here: https://youtu.be/t6oah7P1Fyk it may not seem like much, but this means the lower level systems are working, and I can start focusing on making cool stuff. Other then that I also made the combo system more advanced by having it so u can run actions mid combo and you can detect how fast you did the combo. There are a few more things I want to add to the combo system and tbh I thought about re-coding it again (which is a pretty annoying habit that I’ve gotten #OCD) Next update if I can do it in under 10 hours is gonna be really cool and probably coming tomorrow, following might let u see it earlier :D
Blue Kinda Works! Video Showcase The showcase above includes some of the older versions, skip to 0:40 for official version (it was lagging at the time of recording though) The hardest part of this step was the value adjustments. Given my mod gives each player a random value for cursed energy and , a player needs to feel the diffrence between a weak sorcerer and a strong one. I’ll probably continue tweaking the settings for blue until I believe it is good enough. Anyway right now blue isn’t very complicated, it just detects all entities in a certain radius and gives them a velocity based on the gravitational force which is calculated based on radius and constants. I tried using the real life equations but had trouble with them. Finally, in case your wondering, I am planning on switching the particle renderer that is super laggy with a LWJGL openGL sphere renderer or maybe a block display but in order to do that I need to get good at rendering and make a client linear interpolate to simulate smooth movement and overall I really don’t want to but its gotta happen. Next update will either be a finished blue, or me whining about how trash my code is
MY CODE IS SO TRASH! I was working on fixing my innate technique for limitless. The bug in question was an issue where falling non-living entities would end up rubber-banding before sometimes hitting the ground. (I’m not making a showcase video for this use the screenshot and ur imagination) The issue ended up being due to 2 things, one was that minecraft runs the move() function right after applyGravity() so my code would never get the chance to change the gravity. The second was the fact that move() calls are run both by the server and client. The first issue was relatively simple to fix, I just had to learn about advanced mixins and using @ModifyVariable correctly. However the second issue meant that I either had to send a ton of data with packets, or I could do what I had always wanted to do but knew it would take forever… code in client side compatibility. Anyway as of right now I am “almost” done with getting it working. I swear one of these days I am going to share something cool. probably next update or the one after, it’ll come out by Monday if I lock in.
Limitless Innate Upgrades [upgrades showcase] ( https://youtu.be/m7zWsrxv2mA ) uh the devlog link is broken lol (or im just bad idk). I could talk about the things that I worked on behind the scenes unrelated to the innate upgrades but that’s very boring and I talked about them before, the main thing that I fixed was the handling of non-Living Entities with the limitless innate. Basically I used mixins to edit the move code as well as added some things to the client code. I talked a lot about how this worked in a previous devlog, but it took a lot of reading and understanding code to learn how to fix it. Unfortunately my command blocks were broken so I couldn’t showcase the projectile innate system, but otherwise most of the info is in the video
Sorry, this update will probably be really boring. I’ve been sick for the past 3ish days but have still been trying to code. Unfortunately, that means most of my code is pretty bad. The basic rundown of what’s been happening is a realization that player data was being handled really stupidly. I wasn’t even saving things to files meaning once the server shut down, all the information would be deleted. Now I thought at first I could pretty easily deal with this after finishing the mod so I ignored it, but then I started having issues that came from synchronization with client and server. To those of you who looked at my previous devlogs, you can see that I constantly rewrote a lot of my code. This is because sometimes the client and server use the same static variables while other times they don’t. As a result of these two things, I decided to use a library to handle it called Cardinal Components. For most of my coding projects I’ve always wanted to ignore libraries to the best of my abilities and make stuff myself. I believe it’s a good to learn how the internals of a system work but the problem with me was that I would always stay low level. As I learned more about minecraft development, I learned how important it is to have both high and low level skills in order to make bigger projects. There will probably be only 2 or 3 more devlogs before I ship so I’ll try to get as much cool stuff done before then as possible. Big things are coming!
Finished Cardinal Component re-design. It was really weird to get it working because I needed to use an interface and cast variables which I used to avoid doing. Nothing new unfortunately, next update will go into more detail on how I did this! (with new system this /stats command works :D)
Near completion! It’s weird getting a feeling of satisfaction when deleting the most important part of my code. Thats what I felt when deleting the static variables that stored my data. All of the internal systems are now working and my code is so much cleaner. Now entity data is saved successfully, if you die, leave the world or exit the game. In order to do this I had to learn how to use interfaces well. Turns out casting is a lot more useful then one might expect. You can cast interfaces into classes and vise versa and you can use interfaces with getter and setter methods to get data from classes. I always though interfaces were a useless thing that people use to make better looking code but they are helpful. Anyway, I faced a few major bugs throughout the transition into CCA, the main one being the fact that I had to put an entrypoint in the fabric.mod.json file to render entities. It took a long time and I only found it because of some random reddit thread I found. The main issue was that when I implemented an interface, it was supposed to save data for that class in all entities. Heres a tip, if something is wrong and you can’t find the issue, think about whats happening under the hood. For me, I assumed that the entity data would automatically be initialized but interfaces can’t do that. I’m probably explaining it wrong (I’m a little tired rn with coding and college apps). Anyway, theres a lot more I want to do and I wish that stardance had lasted another week so that I could show you all my cool Ideas. I’ll make the final devlog and ship my project later today. So hopefully it works well :D (I worked on a lot of other stuff in these 10 hours but none are finished enough to devlog about; images have a peak)
JJK Mod in minecraft This is the final devlog here is a youtube link to the showcase: Showcase After finishing the backend, I got a lot of progress done! I recommend watching the showcase but I managed to easily get all the abilities to a semi functional state. Thanks to the hard work of previous me, this wasn’t a hard task and I had no major problems. One interesting thing I did was how I worked with red. I used dot products to make red more usable, it actually pushes enemies back like in the show if they are in the same area as the direction red is moving. Other then that the systems were pretty easy to make. A big reason it took so long to get here was because I had no idea how to mod. Unfortunately in order to get everything done today, I had to cut a few corners, meaning that the power renderer is still a particle system. I could go on and on about what i want to add though (that will be in the ship) so I’ll just end it here
Build and Read me update
Ships:
FULL SHOWCASE HERE This is a Minecraft mod based on the power system from Jujitsu Kaisen. It contains a combo system where if you press a number of keys in the right order, the power will activate. as well as lots of room for expansion with other powers thanks to how it’s built. Currently there are 5 functional abilities: The innate technique which slows down entities around the player; the blue technique which attracts entities to a ball; the red technique which repels enemies away from the user; the purple technique which destroys everything in it’s path; and the domain which sends enemies to a dimension where they are unable to do anything At first I had just planned on using this project as a blank slate to learn how to mod Minecraft, but eventually I got inspired to make more interesting things, and it led to me making one of my personal favorite fictional power systems, the JJK cursed techniques. It took a while to figure things out, I spend many hours looking through Minecraft source code and other peoples code as well as tutorials but eventually I figured out how to do different things. This was easily the most challenging part of the project, compared to a mathematically intensive project like openGL. I’m proud of so many things with this project. For one it is the first fun project I made (both to code and to play). I also made very good code. After studying Minecraft code and making tons of mistakes, I eventually learned how to make code that was both expandable and readable. I believe that given another week, I would have been able to create 5 other abilities. If you want to try the project, make sure to look through the readMe and install the mod on the 1.21.11M branch. The README provides critical information about how to use and install the mod....
|
1792853457.152559
|
1792939857.152559
|
|
87
|
Pterodactyl Panel (Android App) by @stefdp | Stard Pterodactyl Panel (Android App) by @stefdp | Stardance...
|
stefdp
|
1
|
157.29
|
1608
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-08-07T14:34:43Z", "hours": 106.0, "multiplier": 15.23, "payout": 1608, "blessing": "neutral", "text": "For rating & testing the project, please first read all of the text in the release. I made an Android app to manage your self-hosted Pterodactyl Panel instance (Currently only the client side, later I’ll also add the admin side). For who doesn’t know, Pterodactyl Panel is a panel usually used to manage game servers, but it can run anything, each server is just a docker container. The most challenging part was prob downloading files, and it still confuses me tbh because on the Android Studio emulator it works fine, but when if I try it on my actual phone (Samsung A55 5G) it randomly stops after a few seconds and I can’t seem to understand why", "repo": "https://git.stefdp.com/Stef/Pterodactyl-Panel-Android-App", "demo": "https://git.stefdp.com/Stef/Pterodactyl-Panel-Android-App/releases/latest"}]...
|
[{"post_id": 55643, "ts": "202 [{"post_id": 55643, "ts": "2026-08-27T16:32:55Z", "hours": 2.5833, "text": "Added updated checking For F-Droid flavor it doesn’t do anything For APK flavor it checks my git repo For Play Store flavor it uses play store com.google.android.play:app-update library Updated the app site, available at https://ptero.stefdp.com Updated the changelog & app images in the fastlane folder Added an animation when switching between loading states with circular loading"}, {"post_id": 55485, "ts": "2026-08-27T11:17:55Z", "hours": 1.5147, "text": "Added optional biometric authentication so people can lock their app behind phone code or biometric verification (like fingerprint etc.)"}, {"post_id": 54826, "ts": "2026-08-25T23:53:13Z", "hours": 3.245, "text": "Created nests, nest & nest egg screens For nest egg screen, created 3 tabs: Configuration Variables Install Script it was pretty easy and quick to create those cuz they’re read-only as there’s no API endpoint to edit/delete them Added a back handler in the tabbed screens so that back button now goes to the previous tab instead of the entire previous screen In the client servers screen added a switch to see others’ server (admin only) Fixed capitalization in the getNestEgg function ( eggid -> eggId )"}, {"post_id": 54513, "ts": "2026-08-25T15:20:42Z", "hours": 2.9808, "text": "Created users & user screen Updated the AGP (Android Gradle Plugin) Table headers now properly support multi line text (previously if one text was multiline while everything else wasn’t, all the divider would have a gap on the bottom except for the multiline one) In the client server screen, settings tab the UUID is now aligned to the right Improved screen change when deleting a server/node/user/location (instead of navController.popBackStack() (which basically acts as the back button) i specifically navigate to generic screen (eg. “user” -> “users”) and remove current screen from the back history so in the case that there is no screen to back into (eg if the app is opened via a deep link or stuff like that) it will go to the generic tab anyway) Added pull to refresh in the location screen"}, {"post_id": 53949, "ts": "2026-08-24T17:33:07Z", "hours": 1.9753, "text": "Created server manage tab (only reinstall & suspend/unsuspend cuz there seem to be no API endpoint to toggle install status & transfer) Created server delete tab Added 2 new button types, SUCCESS (green) and WARNING (yellow) In the admin server screen now most tabs are hidden whent he server is installing"}, {"post_id": 53378, "ts": "2026-08-23T22:08:42Z", "hours": 1.3206, "text": "Created the admin server databases tab (it only supports resetting the password and deleting the databases because for creating a database i need the hosts IDs, which there seem to be no API endpoint to get them) Added a modifier param to the Container component In the client server screen added a param to directly switch to the databases tab (this is used when the user goes from the admin server screen to the client server screen) Fixed “Missing Server ID” notification when client server is opened with databases tab as first tab"}, {"post_id": 52489, "ts": "2026-08-22T17:05:09Z", "hours": 1.9144, "text": "Created the server startup tab Changed the way Container components are spaced in the Build Configuration tab and create server popup In the create server popup disabled the docker image select when only 1 image is available"}, {"post_id": 52173, "ts": "2026-08-22T08:15:57Z", "hours": 4.4989, "text": "Created server screen with its: About tab Details tab Build Configuration tab Fixed pull to reload, it was updating the refreshIndex variable before the main resource actually refreshed and this caused the tabs not to refresh unless the user pulled to refresh twice CodeText component now uses default text style & font weight instead of pre-defined ones (for some reasons with pre-defined ones, in the ScrollableTabRow component it adds like a margin on the bottom of the blue active indicator) Updated UpdateServerDetailsBody to mark required fields as non-nullable In the server owner select, fixed the selected user disappearing if it was not in the search results In the allocation selects: Made the UI use alias:port when an alias is available, otherwise will default to ip:port Moved the ip:port text to a variable to avoid writing the same thing twice"}, {"post_id": 50274, "ts": "2026-08-19T13:03:15Z", "hours": 2.3186, "text": "Added server creation Added a searchable property in the Select component that adds a text input to filter the options Created a regex to check node & server names"}, {"post_id": 49934, "ts": "2026-08-18T22:04:46Z", "hours": 1.0625, "text": "All *Include enums are now chainable so doing ListNodesQueryInclude.SERVERS + ListServersQueryInclude.USER returns servers.user Also now instead of <enum>.toQueryString(...) (like ListServersQueryInclude.toQueryString(...) ) i do listOf(...).toQueryString() Used the new include enum chaining in 2 places: node servers tab: it now makes way less HTTP requests since the user, nest & egg are directly returned with the server, saving me from making multiple HTTP requests to fetch all users, nests & eggs (which would’ve taken a long time on a large server that for example has thousands of users, since it was fetching at a rate of 50 users per HTTP request, and it being a mobile app, internet might not be fast too) servers screen, now it fetches the egg variable together with the nest instead of having to fetch the egg’s data Moved ApiErrorResponse in a models folder for better organization this is a code only change so ig i’ll show pics of the code"}, {"post_id": 49856, "ts": "2026-08-18T20:07:04Z", "hours": 5.0992, "text": "Created the servers screen (WIP, inputs are untested and creating servers is not implemented yet) Created node servers tab Added pagination to the list nests HTTP request response Removed useless reset of the allocations in the node allocations tab In the created node popup, switched some inputs description from \"quoted\" test to actual inline code blocks CodeText component now uses CharSequence instead of String so it also supports annotated strings Switched main components from Text to CodeText Application server status is now an enum Updated list nests query to include eggs variables too (i just found out i can do this so i’m gonna prob do in in all the other ones in the next devlog too)"}, {"post_id": 48195, "ts": "2026-08-16T14:20:45Z", "hours": 4.3858, "text": "Created the node allocations tab The servers screen now shows the server description when available The 2FA check now considers 2FA as enabled even if the status code is 400 other than checking the error message (ptero got no way of knowing if 2FA is enabled other than that ) Update the TextInput component to allow showing optional suggestions (used in the assign new allocations popup, IP Address input) The Checkbox component now is more transparent when disabled (from 0.5 to 0.2 alpha) Changed how padding works in the Pager component Updated the Table component to add a param to disable lazy loading (for some reason it was laggier) Updated the libs"}, {"post_id": 47489, "ts": "2026-08-15T13:07:23Z", "hours": 2.2333, "text": "Created the node configuration tab Renamed the CodeText component to CodeBlockText and added params to highlight the content with a specific language Created the CodeText component to have both normal text and inline code text (kinda like markdown when you do this )"}, {"post_id": 47046, "ts": "2026-08-14T17:23:43Z", "hours": 1.6747, "text": "Create the settings tab in the node screen Added enabled state to all the inputs & switches in the create node popup Updated the updateNode function’s body & params since some required params were marked as nullable"}, {"post_id": 46843, "ts": "2026-08-14T11:23:01Z", "hours": 2.9253, "text": "Created the node screen & its about tab Moved node specific endpoints to their own ApiService instead of having them in the ApplicationApiService and added 2 endpoints to respectively fetch system data in v1 and v2 Made it so create new node button is disabled until all locations are fetched"}, {"post_id": 45978, "ts": "2026-08-12T20:16:10Z", "hours": 2.9544, "text": "Created the popup to create a node When fetching the node lists, now it also fetches all the locations (needed for a select in the create popup) Added fastlane metadata (will be used for F-Droid in the future) The Popup component now has a scrollbar Updated the scrollbar so now it only shows up when the container has enough contents to be scrollable Added an arrangement property in the Table ’s headers & row cells so that i can center certain things Updated TextInput and Switch components to make their description text color customizable Updated the workflow to automatically push the .aab file to play store after it’s built and made the release (hopefully, i didn’t test it) Fixed create location popup not clearing the inputs upon closing"}, {"post_id": 45461, "ts": "2026-08-11T23:00:38Z", "hours": 0.5592, "text": "Fixed admins not being able to manage servers subusers’ permissions Fixed not being aple to open sub-directories in the file browser Fixed create location popup not closing after creating the location"}, {"post_id": 45335, "ts": "2026-08-11T18:39:18Z", "hours": 1.6944, "text": "Created nodes screen (missing the popup to create a node for now) App name is now dynamically generated in the build.gradle.kts file (so debug build get the (Debug) suffix) Added an HTTP endpoint to check a node status"}, {"post_id": 45109, "ts": "2026-08-11T11:53:58Z", "hours": 0.4881, "text": "Created a CodeText component to display text as code (kinda like this ) and applied it in all the places where I previously just copy pasted same code Creating an API key now copies the key to the clipboard"}, {"post_id": 45061, "ts": "2026-08-11T10:17:04Z", "hours": 5.8633, "text": "Created the locations screen Created the location screen Took and modified to adapt to this app the Table component, originally from my Zipline app Fixed the pull to refresh, when calling onRefresh it didn’t actually set isRefreshing to true so the spinner would immediately disappear instead of swithcing to the spinner state till it finished loading Removed the page property from functions since the page is already stored in the viewModel so it made no sense to retrieve it from the viewModel just to send it to a function in the viewModel Fixed the formatDate function, it was crashing the app on older Android versions (<= 13) because the date time parser couldn’t understand the timezone (it expected a date like 2026-08-08-T15:43:30Z instead of 2026-08-08-T15:43:30+02:00 ) Added a new primary color variant that looks good enough on light bgs Fixed some multi-line TextInput s being marked as singleLine"}, {"post_id": 42647, "ts": "2026-08-07T14:13:23Z", "hours": 0.4458, "text": "Fixed the pull to refresh box having incorrect padding, so the circle was being hidden by the header and by the time it was out, it was already fully pulled and so it would refresh Updated the README also did some tests with downloading but i failed so i just undid them without committing or anything"}, {"post_id": 42285, "ts": "2026-08-06T22:21:15Z", "hours": 2.0292, "text": "Created a website for the app & a privacy policy (that was needed for the play store app), it’s available at https://ptero.stefdp.com Added a prompt to request the notification permission in the login screen and a button in the settings in case you want to enable notification in a second moment Improved the eventDescription variable in the ActivityDisplay component (i didn’t even know that was possible, i found it out cuz i made a _count variable and android studio suggested me to put it directly in the when , lmao) Updated versioning to follow SemVer ( 1.0 -> 1.0.0 )"}, {"post_id": 41895, "ts": "2026-08-06T12:09:27Z", "hours": 3.0606, "text": "in the account settings screen added a tab for the app settings Fixed some activity log event having incorrect names and causing the app to crash Added an action to build the app when the commit is prefixed with [build] or [release] Moved the *Scrollbar modifiers above any padding modifier so they look better Switch ScrollableTabRow to a normal Row instead of LazyRow since it’ll never have enough items to cause any lag Updated the README Removed the admin input in the login screen since it’s currently not available"}, {"post_id": 41497, "ts": "2026-08-05T19:00:04Z", "hours": 1.0289, "text": "In the account settings screen created the activity tab (i think i finished all the client side of the app , now i just gotta finish the small importance TODOs like removing the padding to the scrollbar in most scrollable views & stuff like that) Moved ActivityDisplay in the general components as it’s used in the accoutn activity too Updated ActivityDisplay to include the user agent button when available"}, {"post_id": 41417, "ts": "2026-08-05T16:42:56Z", "hours": 0.65, "text": "In the account settings screen added the SSH Keys tab (basically a almost full copy paste of the API Credentials for the frontend) Fixed capitalization of a variable & function in the api key tab"}, {"post_id": 41037, "ts": "2026-08-04T23:29:22Z", "hours": 2.9483, "text": "In the account settings screen created the API credentials tab Update the server screen so each tabs remembers its own data after switching to another tab (previously it would reset when the user switched to another tab). Also tabs now only fetch data on first render, then they use the previously fetched data instead of re-fetching, unless the user manually refreshes via the “pull to refresh” action (the thing where you swipe from top to down and it shows the refresh circle, idk it’s “professional” name ) Applied same changes as above to the account settings screen Updated the formatDate function to include a param to toggle the year in the date"}, {"post_id": 40669, "ts": "2026-08-04T11:04:30Z", "hours": 5.4211, "text": "Added the account settings screen and its account tab (now missing “API Credentials”, “SSH Keys” and “Activity” tabs, then the app is done ) in the server screen’s files tab file names are now limited to one line and if they overflow, they get cut off and “…” is added Now each warning popup in the server screen has its own icon instead of all using the suspended icon in all places, replaced refreshIndex += 1 with refreshIndex++ The app now no longer shows “Missing Server ID” toast as soon as you open a server Fixed disable 2FA endpoint (i forgot to add /disable at the end and i was wondering why 2FA wasn’t getting disabled even tho the request succeeded (without /disable it acts as enable endpoint)) Fixed get qr code response as it was different from what the API docs listed In the get qr code function added a secondary response to tell that 2FA is already enabled (used to check 2FA status so i know whether to show enable or disable button in the 2FA cuz the user object doesn’t tell me if the user has 2FA on … shitty API fr) Added a simple regex to parse emails (so the change email button is enabled only if this regex passes) Servers screen now uses a normal Column instead of a LazyColumn as lazily loading the server was causing more lag than loading all at once (after all it’s just 10 servers per page) Same as above for the server screen activity tab (jusy 25 logs per page) Fixed the pagination system in servers screen and server screen activity tab (for total amount of pages it was using the total amount of items in the current pages ( .total ) instead of the total number of pages ( .totalPages ), idk how i missed that ) Moved the file picker stuff outside of the LazyColumn & put the permissions in a remember to hopefully try to decrease lag I wanted to make a screen rec but i was too lazy to then go and censor my 2FA secret"}, {"post_id": 39547, "ts": "2026-08-02T15:55:34Z", "hours": 5.2503, "text": "Created the activity tab (finally finished all server tabs :D) along with a function to parse the variables in the logs’ description Improved loading for schedules, backup and databases tabs Fixed JSON text highlighting not highlighting strings inside of arrays Added padding for the Waterfall Cutout (aka screens with rounded edges) in the Header and ScrollableTabRow components In the settings tab added a popup to confirm the reinstallation of the server Added the endpoint to get the server activity (idk how i had skipped that one) and its function to call it Renamed AccountActivity to ActivityLog as it’s used in server activity too Installed a lib to generate avatars thta look like “Boring Avatars” one (cuz boring avatars API is paid ) add .trim() to all user inputs Now tabs refresh only when the server ID changes instead of anything in the server object"}, {"post_id": 39101, "ts": "2026-08-01T20:10:32Z", "hours": 4.3317, "text": "Create a custom PullToRefreshBox component that just uses Material3 PullToRefreshBox under the hood, i just made a component so i can easily style it as it’s in 1 single place Added the pull to refresh gesture to servers list & server details screens Added a warning for when the server is suspended, installing, transferring, restoring from a backup or its node is under maintenance (all warnings for now use same icons, i gotta change that) In the servers list servers now are disable and show a pill when they are suspended, installing, transferring, restoring from a backup or their node is under maintenance (basically when the warning is displayed, the server is also disabled aka you can’t access its data like console, files etc.) The app now properly handles padding for devices that have a “waterfall inset” (aka those kind of phones with curved screen at the edge) The top bar in the server list now properly shows a loading state instead of just disappearing and appearing when the user permissions are fetched Created a function hasPermission to check if an user has a certain permission instead of repeating the same code like 20 times Moved StartupContainer to a more general Container Moved ServerStatus enum to Server.Status Removed navController from all tabs as it was unused"}, {"post_id": 38795, "ts": "2026-08-01T11:14:01Z", "hours": 2.0736, "text": "Created the startup tab Made startup variable description nullable as it’s an optional property"}, {"post_id": 38468, "ts": "2026-07-31T21:10:19Z", "hours": 0.7469, "text": "Network tab now just disables all the buttons when loading instead of switching to skeleton loading for any small change like updating an allocation’s notes Doubled hamburger menu icon left padding to match the avatar padding on the right Doubled the gaps between the cards in the backups & network tabs Halved the gaps between the cards in the schedules tab I also tried to make the TextInput label not squared for a while but failed 😭 , for some reasons even if i clip my highest component to rounded corners, it still has the squared bg in the back"}, {"post_id": 38370, "ts": "2026-07-31T18:28:20Z", "hours": 1.9514, "text": "Added the network tab to manage server allocations Added a “Tertiary” button type (just cuz secondary was almost invisible on the allocation bg 🥀) Fixed allocation endpoints using String instead of Long Moved backup popups outside of the padded Column Updated my github username in the readme"}, {"post_id": 36009, "ts": "2026-07-27T20:28:50Z", "hours": 0.4436, "text": "After i tested more the additions of the last devlog i found some issues so here i fixed them Fixed backups downloading as .gz instead of .tar.gz Fixed “Create Backup” not showing up when the current backup count was 0 (making it impossible to create backups at all) Disabled inputs in the restore & create popups when respectively restoring and creating Removed the enabled param in the BackupDisplay as it was basically useless"}, {"post_id": 35992, "ts": "2026-07-27T19:54:26Z", "hours": 4.4839, "text": "Finished the users tab Made the backups tab Made all popup’s horizontal padding equal When editing or creating a new subuser, the permissins that the current user doesn’t have are disabled in the permissions list Disabled the “Create Database” button when at the limit Renamed File to FileDisplay for consistency with other components Fixed edit & create user body still using String instead of ServerSubuser.Permission Fixed the deleteServerBackup function which was marked as GET instead of DELETE Made the truncate param required in the restoreServerBackup function as it’s required in the API"}, {"post_id": 34673, "ts": "2026-07-25T16:12:15Z", "hours": 5.4628, "text": "Started making the users tab but it’s really WIP, none of the buttons except create new user works) Handled subuser permissions for all current tabs Added a LabeledCheckbox component Removed some function params that were pointless to have Installed the Gravatar SDK for the subusers’ avatar I legit think Android Studio is ragebaiting me, as soon as i start the rec the app starts to lag, when i stop the rec it’s fine"}, {"post_id": 34147, "ts": "2026-07-24T17:24:53Z", "hours": 3.77, "text": "Finished handling buttons in the schedules tab & created all the popups for schedule & tasks management In all screens & tab now all the inputs, switches, selects etc. are disabled when loading or making a HTTP request (like delete schedule, update file name etc.) Added a function to convert ISO date strings in human-readable strings Added a missing endpoint to execute a schedule Added a missing only_when_online property in the ServerSchedule data class Fixed schedule & tasks endpoint types (was using String instead of Long for the IDs) Fixed ScrollableTabRow not using all the horizontal space when in landscape mode"}, {"post_id": 33800, "ts": "2026-07-24T00:56:20Z", "hours": 2.5242, "text": "Finished the UI side of the schedule details popup (still WIP for backend like button handling) Moved each popup into its own folder & file to avoid having many long Popup calls at the top of screen/tab files Changed task action from a normal screen to an enum Took the formatDate function from my Zipline app"}, {"post_id": 33067, "ts": "2026-07-22T17:17:14Z", "hours": 4.2242, "text": "Created the schedules tab for the server screen (still WIP, for now only works for displaying & creating schedules, gotta add edit, deletion and task management) Moved each tab to its own ViewModel for better ordering instead of putting everything is a generic one Improved code layout by using return s in some places instead of many if else blocks Fixed ScrollableTabRow component improperly placing icons when there was no text Moved ‘linked’ data class es inside each other so i can do Server.Attributes.Limits instead of having a class with a long name like ApplicationServerAttributesLimits"}, {"post_id": 32681, "ts": "2026-07-22T00:08:40Z", "hours": 5.6317, "text": "Updated the file edit screen to use VSCode’s Dark+ theme colors for highlighting and added Java and CASM/Crawssembly by @The_Craw Added the databases tab with handling for creation, deletion and rotation of the password Fixed rotateServerDatabasePassword and deleteServerDatabase , they were using the wrong endpoint ( /server instead of /servers ) Made each tab independently scrollable instead of making the parent component scrollable Took the IconButton component from my Zipline app and slightly modified it (the MoreActionsButton component now uses that) Changed the Notification object component’s background color"}, {"post_id": 31859, "ts": "2026-07-20T16:06:12Z", "hours": 2.1247, "text": "FInished file handling (handled edit & save files & create new file) Added file highlighting for .env files Fixed writeServerFile having a wrong Content-Type (it had application/json instead of text/plain since the input is a raw string) Fixed HTML syntax highlighting, it wasn’t highlighting the > part of the tag"}, {"post_id": 31721, "ts": "2026-07-20T10:47:03Z", "hours": 2.645, "text": "Added a screen for code editing with simple syntax highlighting (via a newly installed library) and a warning if you try to leave without saving (that’s still WIP, saving isn’t implemented yet and neither reading the content of the selected file) Updated the TextInput component to have a fontFamily property so that in the core editor i can set the text to be monospace On the login screen i disabled the application api key input till the admin side is actually done"}, {"post_id": 31407, "ts": "2026-07-19T19:00:46Z", "hours": 4.4347, "text": "(Sorry for the slightly laggy video, not sure if it’s cuz it’s via a emulator or cuz i compressed the video from 136 MiB down to 36 MB) In the server screen added the handling for download, upload, delete, copy, create folder, rename/move, change permissions, archive & unarchive Filled the File component’s “more actions” menu (before it was empty) Updated uploadServerFiles to be able to upload multiple files at once Added a function to convert linux string permissions (like drwxr-xr-x ) to int/octal (like 755 ) Added a regex for the linux int/octal permission Took the Popup component and Storage object form my Zipline app Updated the SecureStorage class to add the file download folder variable"}, {"post_id": 30962, "ts": "2026-07-18T23:42:06Z", "hours": 2.1631, "text": "Created the Files tab in the server screen (still WIP, most of the buttons do nothing rn) Created a File component to avoid repeating same code Added a new color surfaceContainer used in notifications Took the “More Actions” component from my Zipline app Took the “Checkbox” component from my Zipline app and modified it to be toggleable In server screen disable global scroll on certain tabs as they’d cause the app to crash for infinite scroll moved the WebSocket connection to the Console tab instead of the general server screen, this way it auto reconnects when the user switches back to the console tab (since it disconnects when the user moves to another tab to save resources)"}, {"post_id": 30840, "ts": "2026-07-18T19:40:14Z", "hours": 3.5069, "text": "Added charts to the console tab of the server screen Added a top scrollbar like ptero’s to the server screen and moved the console stuff to its own file for better sorting Created a component for the chart container & top scrollbar to avoid repetition Added AI declaration in the readme cuz i only found out now that it’s required Removed a double new line the Button component"}, {"post_id": 29863, "ts": "2026-07-17T00:51:22Z", "hours": 3.43, "text": "Created the server screen and right now i only made the console section out of all the ones i gotta do (console, files, databases, schedules, users, backups, network, startup, settings & activity) The server screen itself is WIP as it’s missing the buttons to switch between the sections The console tab is also WIP as i haven’t done the line charts for CPU, RAM & Network I/O) (devlogging with this stuff half-made cuz it’s almost 3AM so i gotta sleep ) Took the formatMs function from my zipline app to convert ms to human readable time like “1h 30m 20s” for the server uptime In the WebSocketManager class renamed the setPowerState function to sendPowerSignal as it makes more sense On the console screen i wanted the console to follow its actual colors but only lib i found to do that doesn’t support jetpack compose that well so i had to rely on the XML’s TextView (but in compose thanks to the AndroidView component)"}, {"post_id": 29055, "ts": "2026-07-15T18:23:36Z", "hours": 2.9633, "text": "Fixed websocket not working (apparently it 403’s unless you put the panel URL as Origin header) Added pagination to the servers screen and also made the server load lazily and get unrendered when out of the display Also suspended servers’ names now are grayed out Installed a ANSI parser library to correctly color console output (hopefully it works however they already said it won’t work for outline 😭) Took the scrollbar modifiers from my Zipline app and put a scrollbar in the servers list Created a object to easily access the WebSocketManager from a ViewModel class Currently working on the specific server page, right now it’s empty but i got websocket working, i’ll push it once it’s finished"}, {"post_id": 28568, "ts": "2026-07-14T21:58:36Z", "hours": 3.9736, "text": "Added the UI for the client servers list screen (still gotta finish it tho, just have to add pagination and will only shows 10 servers per page to avoid getting ratelimited as i have to make a separate HTTP request for each server to get their resources usage) Added pterodactyl panel colors to be used in the server status (red, green and yellow) Fixed listServers not working cuz of wrong path (i didn’t know how to tell retrofit to use just the baseUrl , then after it threw an error i googled it and realized i just had to put . ) Updated getServerResources response class, now the server state uses a enum instead of a generic string Updated Server class to better fix some typed and add 1 missing key Added the uploadServerFile HTTP method (i didn’t do i it initially cuz the docs were confusing and i couldn’t understand how to select the upload directory, turns out i was setting the directory query on the request to get the signed url instead of the signed url itself 😭 Improved login process to handle the case when the user logged with just an application api key and not a client one Added application API key validation for the same reason as the login Took shimmerable modifier from my Zipline app to make skeleton loading Added definitions & placeholders for all screens until i make them"}, {"post_id": 28257, "ts": "2026-07-14T12:33:25Z", "hours": 2.8692, "text": "Created Loading and Login Screen After making a HTTP request the app crashed and only then i realized that i didn’t make the HTTP request suspend functions so i fixed it Took the Notification object from my Zipline app, tho i gotta fix it a bit cuz the color is kinda ugly and i still don’t know which one in the palette to use cuz imo none of them fits, maybe i’ll just use a separate color out of the palette for it Updated the TextInput component to add a optional required field which when set to true adds a red * next to the label, might do this with switches too I took some common regex (domain, IPv4, number and decimal) since i might need them later and some of them (domain & IPv4) are also aready used in the login"}, {"post_id": 27777, "ts": "2026-07-13T17:06:27Z", "hours": 2.8914, "text": "Finished creating the methods to call the client API (same as application ones, copy pasted same code and changed method & function vars) (except for the download* ones, i took those from my other app Zipline) I also took the Transfer Service from my Zipline app Updated all the libraries Set target SDK to 37 (Android 17) now i gotta do the frontend part"}, {"post_id": 27272, "ts": "2026-07-12T18:21:50Z", "hours": 0.7419, "text": "Finished creating the methods to call the application API (basically just copy pasted same code and changed the method & the fuction vars) Now i gotta make the same thing for the client API"}, {"post_id": 27163, "ts": "2026-07-12T14:07:41Z", "hours": 1.4042, "text": "Started creating methods to call the API endpoints easily Took the Header component from one of my other apps"}, {"post_id": 26528, "ts": "2026-07-11T12:05:14Z", "hours": 0.8058, "text": "Set up color scheme (right now both light and dark color are the same, idk what colors to use to differ but make it still look like the official app) Added some util modules that i also use in my other apps Created an empty login screen"}, {"post_id": 26500, "ts": "2026-07-11T10:27:23Z", "hours": 0.9733, "text": "I had forgotten to add the client API HTTP endpoint to get the console websocket token & URL so i added it now Added the WebSocket handling (untested) Updated the proguard rules so that HTTP request/response classes are not shrunk or minified by R6"}, {"post_id": 26100, "ts": "2026-07-10T18:05:31Z", "hours": 0.6336, "text": "Finished adding the application API HTTP requests (added application/servers/{serverId}/databases , application/nests and application/nests/{nestId}/eggs ) Also fixed some classes having wrong data cuz i realized that i copy pasted them without actually changing their name in some places 😭 you can see them in the commit here even tho i’m kinda confused cuz in the docs i didn’t see any endpoint to create or manage nests, eggs and database hosts"}, {"post_id": 26051, "ts": "2026-07-10T17:14:07Z", "hours": 2.1628, "text": "Added more application API HTTP requests (all the application/nodes/{nodeId}/allocations , application/locations and application/servers endpoints) you can see them in the commit here"}, {"post_id": 21431, "ts": "2026-07-03T11:41:26Z", "hours": 2.1569, "text": "Added more application API HTTP requests (all the /application/user and /application/nodes endpoints) you can see them in the commit here"}, {"post_id": 7486, "ts": "2026-06-12T14:24:53Z", "hours": 1.1056, "text": "Split the API in 2 types: application and client (as pterodactyl handles those differently with 2 diff api keys) Fixed some wrong or missing types Finished adding the HTTP requests for the client API Started adding the HTTP requests for the application API"}, {"post_id": 7451, "ts": "2026-06-12T12:46:34Z", "hours": 1.6653, "text": "Added more HTTP requests (ngl am too lazy to list them all, just watch them in the commit here )"}, {"post_id": 4342, "ts": "2026-06-07T16:58:23Z", "hours": 4.2092, "text": "Added even more HTTP requests, tho could be not 100% accurate cuz ptero API is shit 😭, the official one is AI generated and has hallucinations…. anyway i added: GET /api/client/account/activity GET /api/client/account/ssh-keys POST /api/client/account/ssh-keys POST /api/client/account/ssh-keys/remove GET /api/client/servers/{identifier} GET /api/client/servers/{identifier}/resources POST /api/client/servers/{identifier}/command POST /api/client/servers/{identifier}/power GET /api/client/servers/{identifier}/databases POST /api/client/servers/{identifier}/databases POST /api/client/server/{serverIdentifier}/databases/{databaseIdentifier)/rotate-password DELETE /api/client/server/{serverIdentifier}/databases/{databaseIdentifier} GET /api/client/servers/{identifier}/files/list GET /api/client/servers/{identifier}/files/contents GET /api/client/servers/{identifier}/files/download POST /api/client/servers/{identifier}/files/write POST /api/client/servers/{identifier}/files/create-folder POST /api/client/servers/{identifier}/files/copy PUT /api/client/servers/{identifier}/files/rename POST /api/client/servers/{identifier}/files/compress POST /api/client/servers/{identifier}/files/decompress POST /api/client/servers/{identifier}/files/delete GET /api/client/servers/{identifier}/files/upload POST /api/client/servers/{identifier}/files/chmod GET /api/client/servers/{identifier}/schedules GET /api/client/servers/{identifier}/schedules/{scheduleIdentifier} POST /api/client/servers/{identifier}/schedules POST /api/client/servers/{identifier}/schedules/{scheduleIdentifier} DELETE /api/client/servers/{identifier}/schedules/{scheduleIdentifier} POST /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasks POST /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasks/{taskIdentifier} DELETE /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasks/{taskIdentifier} Writing all these took alot of time"}, {"post_id": 3759, "ts": "2026-06-06T20:13:46Z", "hours": 1.5667, "text": "Started adding some HTTP requests typedef, still missing alot tho for now i added: GET /api/client GET /api/client/permissions GET /api/client/account GET /api/client/account/two-factor POST /api/client/account/two-factor (enable) POST /api/client/account/two-factor (disable) PUT /api/client/account/email PUT /api/client/account/password GET /api/client/account/api-keys DELETE /api/client/account/api-keys/{identifier} POST /api/client/account/api-keys no frontend yet, i’m doing the backend first"}, {"post_id": 3611, "ts": "2026-06-06T16:44:59Z", "hours": 0.7236, "text": "Just created the app skeleton based on the other 2 apps i’ve already made (with alot of TODOs for now) now working on writing the code for HTTP requests to the pterodactyl API and their response types"}]...
|
Title: Pterodactyl Panel (Android App) by @stefdp Title: Pterodactyl Panel (Android App) by @stefdp | Stardance
Devlogs:
Just created the app skeleton based on the other 2 apps i’ve already made (with alot of TODOs for now) now working on writing the code for HTTP requests to the pterodactyl API and their response types
Started adding some HTTP requests typedef, still missing alot tho for now i added: GET /api/client GET /api/client/permissions GET /api/client/account GET /api/client/account/two-factor POST /api/client/account/two-factor (enable) POST /api/client/account/two-factor (disable) PUT /api/client/account/email PUT /api/client/account/password GET /api/client/account/api-keys DELETE /api/client/account/api-keys/{identifier} POST /api/client/account/api-keys no frontend yet, i’m doing the backend first
Added even more HTTP requests, tho could be not 100% accurate cuz ptero API is shit 😭, the official one is AI generated and has hallucinations…. anyway i added: GET /api/client/account/activity GET /api/client/account/ssh-keys POST /api/client/account/ssh-keys POST /api/client/account/ssh-keys/remove GET /api/client/servers/{identifier} GET /api/client/servers/{identifier}/resources POST /api/client/servers/{identifier}/command POST /api/client/servers/{identifier}/power GET /api/client/servers/{identifier}/databases POST /api/client/servers/{identifier}/databases POST /api/client/server/{serverIdentifier}/databases/{databaseIdentifier)/rotate-password DELETE /api/client/server/{serverIdentifier}/databases/{databaseIdentifier} GET /api/client/servers/{identifier}/files/list GET /api/client/servers/{identifier}/files/contents GET /api/client/servers/{identifier}/files/download POST /api/client/servers/{identifier}/files/write POST /api/client/servers/{identifier}/files/create-folder POST /api/client/servers/{identifier}/files/copy PUT /api/client/servers/{identifier}/files/rename POST /api/client/servers/{identifier}/files/compress POST /api/client/servers/{identifier}/files/decompress POST /api/client/servers/{identifier}/files/delete GET /api/client/servers/{identifier}/files/upload POST /api/client/servers/{identifier}/files/chmod GET /api/client/servers/{identifier}/schedules GET /api/client/servers/{identifier}/schedules/{scheduleIdentifier} POST /api/client/servers/{identifier}/schedules POST /api/client/servers/{identifier}/schedules/{scheduleIdentifier} DELETE /api/client/servers/{identifier}/schedules/{scheduleIdentifier} POST /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasks POST /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasks/{taskIdentifier} DELETE /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasks/{taskIdentifier} Writing all these took alot of time
Added more HTTP requests (ngl am too lazy to list them all, just watch them in the commit here )
Split the API in 2 types: application and client (as pterodactyl handles those differently with 2 diff api keys) Fixed some wrong or missing types Finished adding the HTTP requests for the client API Started adding the HTTP requests for the application API
Added more application API HTTP requests (all the /application/user and /application/nodes endpoints) you can see them in the commit here
Added more application API HTTP requests (all the application/nodes/{nodeId}/allocations , application/locations and application/servers endpoints) you can see them in the commit here
Finished adding the application API HTTP requests (added application/servers/{serverId}/databases , application/nests and application/nests/{nestId}/eggs ) Also fixed some classes having wrong data cuz i realized that i copy pasted them without actually changing their name in some places 😭 you can see them in the commit here even tho i’m kinda confused cuz in the docs i didn’t see any endpoint to create or manage nests, eggs and database hosts
I had forgotten to add the client API HTTP endpoint to get the console websocket token & URL so i added it now Added the WebSocket handling (untested) Updated the proguard rules so that HTTP request/response classes are not shrunk or minified by R6
Set up color scheme (right now both light and dark color are the same, idk what colors to use to differ but make it still look like the official app) Added some util modules that i also use in my other apps Created an empty login screen
Started creating methods to call the API endpoints easily Took the Header component from one of my other apps
Finished creating the methods to call the application API (basically just copy pasted same code and changed the method & the fuction vars) Now i gotta make the same thing for the client API
Finished creating the methods to call the client API (same as application ones, copy pasted same code and changed method & function vars) (except for the download* ones, i took those from my other app Zipline) I also took the Transfer Service from my Zipline app Updated all the libraries Set target SDK to 37 (Android 17) now i gotta do the frontend part
Created Loading and Login Screen After making a HTTP request the app crashed and only then i realized that i didn’t make the HTTP request suspend functions so i fixed it Took the Notification object from my Zipline app, tho i gotta fix it a bit cuz the color is kinda ugly and i still don’t know which one in the palette to use cuz imo none of them fits, maybe i’ll just use a separate color out of the palette for it Updated the TextInput component to add a optional required field which when set to true adds a red * next to the label, might do this with switches too I took some common regex (domain, IPv4, number and decimal) since i might need them later and some of them (domain & IPv4) are also aready used in the login
Added the UI for the client servers list screen (still gotta finish it tho, just have to add pagination and will only shows 10 servers per page to avoid getting ratelimited as i have to make a separate HTTP request for each server to get their resources usage) Added pterodactyl panel colors to be used in the server status (red, green and yellow) Fixed listServers not working cuz of wrong path (i didn’t know how to tell retrofit to use just the baseUrl , then after it threw an error i googled it and realized i just had to put . ) Updated getServerResources response class, now the server state uses a enum instead of a generic string Updated Server class to better fix some typed and add 1 missing key Added the uploadServerFile HTTP method (i didn’t do i it initially cuz the docs were confusing and i couldn’t understand how to select the upload directory, turns out i was setting the directory query on the request to get the signed url instead of the signed url itself 😭 Improved login process to handle the case when the user logged with just an application api key and not a client one Added application API key validation for the same reason as the login Took shimmerable modifier from my Zipline app to make skeleton loading Added definitions & placeholders for all screens until i make them
Fixed websocket not working (apparently it 403’s unless you put the panel URL as Origin header) Added pagination to the servers screen and also made the server load lazily and get unrendered when out of the display Also suspended servers’ names now are grayed out Installed a ANSI parser library to correctly color console output (hopefully it works however they already said it won’t work for outline 😭) Took the scrollbar modifiers from my Zipline app and put a scrollbar in the servers list Created a object to easily access the WebSocketManager from a ViewModel class Currently working on the specific server page, right now it’s empty but i got websocket working, i’ll push it once it’s finished
Created the server screen and right now i only made the console section out of all the ones i gotta do (console, files, databases, schedules, users, backups, network, startup, settings & activity) The server screen itself is WIP as it’s missing the buttons to switch between the sections The console tab is also WIP as i haven’t done the line charts for CPU, RAM & Network I/O) (devlogging with this stuff half-made cuz it’s almost 3AM so i gotta sleep ) Took the formatMs function from my zipline app to convert ms to human readable time like “1h 30m 20s” for the server uptime In the WebSocketManager class renamed the setPowerState function to sendPowerSignal as it makes more sense On the console screen i wanted the console to follow its actual colors but only lib i found to do that doesn’t support jetpack compose that well so i had to rely on the XML’s TextView (but in compose thanks to the AndroidView component)
Added charts to the console tab of the server screen Added a top scrollbar like ptero’s to the server screen and moved the console stuff to its own file for better sorting Created a component for the chart container & top scrollbar to avoid repetition Added AI declaration in the readme cuz i only found out now that it’s required Removed a double new line the Button component
Created the Files tab in the server screen (still WIP, most of the buttons do nothing rn) Created a File component to avoid repeating same code Added a new color surfaceContainer used in notifications Took the “More Actions” component from my Zipline app Took the “Checkbox” component from my Zipline app and modified it to be toggleable In server screen disable global scroll on certain tabs as they’d cause the app to crash for infinite scroll moved the WebSocket connection to the Console tab instead of the general server screen, this way it auto reconnects when the user switches back to the console tab (since it disconnects when the user moves to another tab to save resources)
(Sorry for the slightly laggy video, not sure if it’s cuz it’s via a emulator or cuz i compressed the video from 136 MiB down to 36 MB) In the server screen added the handling for download, upload, delete, copy, create folder, rename/move, change permissions, archive & unarchive Filled the File component’s “more actions” menu (before it was empty) Updated uploadServerFiles to be able to upload multiple files at once Added a function to convert linux string permissions (like drwxr-xr-x ) to int/octal (like 755 ) Added a regex for the linux int/octal permission Took the Popup component and Storage object form my Zipline app Updated the SecureStorage class to add the file download folder variable
Added a screen for code editing with simple syntax highlighting (via a newly installed library) and a warning if you try to leave without saving (that’s still WIP, saving isn’t implemented yet and neither reading the content of the selected file) Updated the TextInput component to have a fontFamily property so that in the core editor i can set the text to be monospace On the login screen i disabled the application api key input till the admin side is actually done
FInished file handling (handled edit & save files & create new file) Added file highlighting for .env files Fixed writeServerFile having a wrong Content-Type (it had application/json instead of text/plain since the input is a raw string) Fixed HTML syntax highlighting, it wasn’t highlighting the > part of the tag
Updated the file edit screen to use VSCode’s Dark+ theme colors for highlighting and added Java and CASM/Crawssembly by @The_Craw Added the databases tab with handling for creation, deletion and rotation of the password Fixed rotateServerDatabasePassword and deleteServerDatabase , they were using the wrong endpoint ( /server instead of /servers ) Made each tab independently scrollable instead of making the parent component scrollable Took the IconButton component from my Zipline app and slightly modified it (the MoreActionsButton component now uses that) Changed the Notification object component’s background color
Created the schedules tab for the server screen (still WIP, for now only works for displaying & creating schedules, gotta add edit, deletion and task management) Moved each tab to its own ViewModel for better ordering instead of putting everything is a generic one Improved code layout by using return s in some places instead of many if else blocks Fixed ScrollableTabRow component improperly placing icons when there was no text Moved ‘linked’ data class es inside each other so i can do Server.Attributes.Limits instead of having a class with a long name like ApplicationServerAttributesLimits
Finished the UI side of the schedule details popup (still WIP for backend like button handling) Moved each popup into its own folder & file to avoid having many long Popup calls at the top of screen/tab files Changed task action from a normal screen to an enum Took the formatDate function from my Zipline app
Finished handling buttons in the schedules tab & created all the popups for schedule & tasks management In all screens & tab now all the inputs, switches, selects etc. are disabled when loading or making a HTTP request (like delete schedule, update file name etc.) Added a function to convert ISO date strings in human-readable strings Added a missing endpoint to execute a schedule Added a missing only_when_online property in the ServerSchedule data class Fixed schedule & tasks endpoint types (was using String instead of Long for the IDs) Fixed ScrollableTabRow not using all the horizontal space when in landscape mode
Started making the users tab but it’s really WIP, none of the buttons except create new user works) Handled subuser permissions for all current tabs Added a LabeledCheckbox component Removed some function params that were pointless to have Installed the Gravatar SDK for the subusers’ avatar I legit think Android Studio is ragebaiting me, as soon as i start the rec the app starts to lag, when i stop the rec it’s fine
Finished the users tab Made the backups tab Made all popup’s horizontal padding equal When editing or creating a new subuser, the permissins that the current user doesn’t have are disabled in the permissions list Disabled the “Create Database” button when at the limit Renamed File to FileDisplay for consistency with other components Fixed edit & create user body still using String instead of ServerSubuser.Permission Fixed the deleteServerBackup function which was marked as GET instead of DELETE Made the truncate param required in the restoreServerBackup function as it’s required in the API
After i tested more the additions of the last devlog i found some issues so here i fixed them Fixed backups downloading as .gz instead of .tar.gz Fixed “Create Backup” not showing up when the current backup count was 0 (making it impossible to create backups at all) Disabled inputs in the restore & create popups when respectively restoring and creating Removed the enabled param in the BackupDisplay as it was basically useless
Added the network tab to manage server allocations Added a “Tertiary” button type (just cuz secondary was almost invisible on the allocation bg 🥀) Fixed allocation endpoints using String instead of Long Moved backup popups outside of the padded Column Updated my github username in the readme
Network tab now just disables all the buttons when loading instead of switching to skeleton loading for any small change like updating an allocation’s notes Doubled hamburger menu icon left padding to match the avatar padding on the right Doubled the gaps between the cards in the backups & network tabs Halved the gaps between the cards in the schedules tab I also tried to make the TextInput label not squared for a while but failed 😭 , for some reasons even if i clip my highest component to rounded corners, it still has the squared bg in the back
Created the startup tab Made startup variable description nullable as it’s an optional property
Create a custom PullToRefreshBox component that just uses Material3 PullToRefreshBox under the hood, i just made a component so i can easily style it as it’s in 1 single place Added the pull to refresh gesture to servers list & server details screens Added a warning for when the server is suspended, installing, transferring, restoring from a backup or its node is under maintenance (all warnings for now use same icons, i gotta change that) In the servers list servers now are disable and show a pill when they are suspended, installing, transferring, restoring from a backup or their node is under maintenance (basically when the warning is displayed, the server is also disabled aka you can’t access its data like console, files etc.) The app now properly handles padding for devices that have a “waterfall inset” (aka those kind of phones with curved screen at the edge) The top bar in the server list now properly shows a loading state instead of just disappearing and appearing when the user permissions are fetched Created a function hasPermission to check if an user has a certain permission instead of repeating the same code like 20 times Moved StartupContainer to a more general Container Moved ServerStatus enum to Server.Status Removed navController from all tabs as it was unused
Created the activity tab (finally finished all server tabs :D) along with a function to parse the variables in the logs’ description Improved loading for schedules, backup and databases tabs Fixed JSON text highlighting not highlighting strings inside of arrays Added padding for the Waterfall Cutout (aka screens with rounded edges) in the Header and ScrollableTabRow components In the settings tab added a popup to confirm the reinstallation of the server Added the endpoint to get the server activity (idk how i had skipped that one) and its function to call it Renamed AccountActivity to ActivityLog as it’s used in server activity too Installed a lib to generate avatars thta look like “Boring Avatars” one (cuz boring avatars API is paid ) add .trim() to all user inputs Now tabs refresh only when the server ID changes instead of anything in the server object
Added the account settings screen and its account tab (now missing “API Credentials”, “SSH Keys” and “Activity” tabs, then the app is done ) in the server screen’s files tab file names are now limited to one line and if they overflow, they get cut off and “…” is added Now each warning popup in the server screen has its own icon instead of all using the suspended icon in all places, replaced refreshIndex += 1 with refreshIndex++ The app now no longer shows “Missing Server ID” toast as soon as you open a server Fixed disable 2FA endpoint (i forgot to add /disable at the end and i was wondering why 2FA wasn’t getting disabled even tho the request succeeded (without /disable it acts as enable endpoint)) Fixed get qr code response as it was different from what the API docs listed In the get qr code function added a secondary response to tell that 2FA is already enabled (used to check 2FA status so i know whether to show enable or disable button in the 2FA cuz the user object doesn’t tell me if the user has 2FA on … shitty API fr) Added a simple regex to parse emails (so the change email button is enabled only if this regex passes) Servers screen now uses a normal Column instead of a LazyColumn as lazily loading the server was causing more lag than loading all at once (after all it’s just 10 servers per page) Same as above for the server screen activity tab (jusy 25 logs per page) Fixed the pagination system in servers screen and server screen activity tab (for total amount of pages it was using the total amount of items in the current pages ( .total ) instead of the total number of pages ( .totalPages ), idk how i missed that ) Moved the file picker stuff outside of the LazyColumn & put the permissions in a remember to hopefully try to decrease lag I wanted to make a screen rec but i was too lazy to then go and censor my 2FA secret
In the account settings screen created the API credentials tab Update the server screen so each tabs remembers its own data after switching to another tab (previously it would reset when the user switched to another tab). Also tabs now only fetch data on first render, then they use the previously fetched data instead of re-fetching, unless the user manually refreshes via the “pull to refresh” action (the thing where you swipe from top to down and it shows the refresh circle, idk it’s “professional” name ) Applied same changes as above to the account settings screen Updated the formatDate function to include a param to toggle the year in the date
In the account settings screen added the SSH Keys tab (basically a almost full copy paste of the API Credentials for the frontend) Fixed capitalization of a variable & function in the api key tab
In the account settings screen created the activity tab (i think i finished all the client side of the app , now i just gotta finish the small importance TODOs like removing the padding to the scrollbar in most scrollable views & stuff like that) Moved ActivityDisplay in the general components as it’s used in the accoutn activity too Updated ActivityDisplay to include the user agent button when available
in the account settings screen added a tab for the app settings Fixed some activity log event having incorrect names and causing the app to crash Added an action to build the app when the commit is prefixed with [build] or [release] Moved the *Scrollbar modifiers above any padding modifier so they look better Switch ScrollableTabRow to a normal Row instead of LazyRow since it’ll never have enough items to cause any lag Updated the README Removed the admin input in the login screen since it’s currently not available
Created a website for the app & a privacy policy (that was needed for the play store app), it’s available at https://ptero.stefdp.com Added a prompt to request the notification permission in the login screen and a button in the settings in case you want to enable notification in a second moment Improved the eventDescription variable in the ActivityDisplay component (i didn’t even know that was possible, i found it out cuz i made a _count variable and android studio suggested me to put it directly in the when , lmao) Updated versioning to follow SemVer ( 1.0 -> 1.0.0 )
Fixed the pull to refresh box having incorrect padding, so the circle was being hidden by the header and by the time it was out, it was already fully pulled and so it would refresh Updated the README also did some tests with downloading but i failed so i just undid them without committing or anything
Created the locations screen Created the location screen Took and modified to adapt to this app the Table component, originally from my Zipline app Fixed the pull to refresh, when calling onRefresh it didn’t actually set isRefreshing to true so the spinner would immediately disappear instead of swithcing to the spinner state till it finished loading Removed the page property from functions since the page is already stored in the viewModel so it made no sense to retrieve it from the viewModel just to send it to a function in the viewModel Fixed the formatDate function, it was crashing the app on older Android versions (<= 13) because the date time parser couldn’t understand the timezone (it expected a date like 2026-08-08-T15:43:30Z instead of 2026-08-08-T15:43:30+02:00 ) Added a new primary color variant that looks good enough on light bgs Fixed some multi-line TextInput s being marked as singleLine
Created a CodeText component to display text as code (kinda like this ) and applied it in all the places where I previously just copy pasted same code Creating an API key now copies the key to the clipboard
Created nodes screen (missing the popup to create a node for now) App name is now dynamically generated in the build.gradle.kts file (so debug build get the (Debug) suffix) Added an HTTP endpoint to check a node status
Fixed admins not being able to manage servers subusers’ permissions Fixed not being aple to open sub-directories in the file browser Fixed create location popup not closing after creating the location
Created the popup to create a node When fetching the node lists, now it also fetches all the locations (needed for a select in the create popup) Added fastlane metadata (will be used for F-Droid in the future) The Popup component now has a scrollbar Updated the scrollbar so now it only shows up when the container has enough contents to be scrollable Added an arrangement property in the Table ’s headers & row cells so that i can center certain things Updated TextInput and Switch components to make their description text color customizable Updated the workflow to automatically push the .aab file to play store after it’s built and made the release (hopefully, i didn’t test it) Fixed create location popup not clearing the inputs upon closing
Created the node screen & its about tab Moved node specific endpoints to their own ApiService instead of having them in the ApplicationApiService and added 2 endpoints to respectively fetch system data in v1 and v2 Made it so create new node button is disabled until all locations are fetched
Create the settings tab in the node screen Added enabled state to all the inputs & switches in the create node popup Updated the updateNode function’s body & params since some required params were marked as nullable
Created the node configuration tab Renamed the CodeText component to CodeBlockText and added params to highlight the content with a specific language Created the CodeText component to have both normal text and inline code text (kinda like markdown when you do this )
Created the node allocations tab The servers screen now shows the server description when available The 2FA check now considers 2FA as enabled even if the status code is 400 other than checking the error message (ptero got no way of knowing if 2FA is enabled other than that ) Update the TextInput component to allow showing optional suggestions (used in the assign new allocations popup, IP Address input) The Checkbox component now is more transparent when disabled (from 0.5 to 0.2 alpha) Changed how padding works in the Pager component Updated the Table component to add a param to disable lazy loading (for some reason it was laggier) Updated the libs
Created the servers screen (WIP, inputs are untested and creating servers is not implemented yet) Created node servers tab Added pagination to the list nests HTTP request response Removed useless reset of the allocations in the node allocations tab In the created node popup, switched some inputs description from "quoted" test to actual inline code blocks CodeText component now uses CharSequence instead of String so it also supports annotated strings Switched main components from Text to CodeText Application server status is now an enum Updated list nests query to include eggs variables too (i just found out i can do this so i’m gonna prob do in in all the other ones in the next devlog too)
All *Include enums are now chainable so doing ListNodesQueryInclude.SERVERS + ListServersQueryInclude.USER returns servers.user Also now instead of <enum>.toQueryString(...) (like ListServersQueryInclude.toQueryString(...) ) i do listOf(...).toQueryString() Used the new include enum chaining in 2 places: node servers tab: it now makes way less HTTP requests since the user, nest & egg are directly returned with the server, saving me from making multiple HTTP requests to fetch all users, nests & eggs (which would’ve taken a long time on a large server that for example has thousands of users, since it was fetching at a rate of 50 users per HTTP request, and it being a mobile app, internet might not be fast too) servers screen, now it fetches the egg variable together with the nest instead of having to fetch the egg’s data Moved ApiErrorResponse in a models folder for better organization this is a code only change so ig i’ll show pics of the code
Added server creation Added a searchable property in the Select component that adds a text input to filter the options Created a regex to check node & server names
Created server screen with its: About tab Details tab Build Configuration tab Fixed pull to reload, it was updating the refreshIndex variable before the main resource actually refreshed and this caused the tabs not to refresh unless the user pulled to refresh twice CodeText component now uses default text style & font weight instead of pre-defined ones (for some reasons with pre-defined ones, in the ScrollableTabRow component it adds like a margin on the bottom of the blue active indicator) Updated UpdateServerDetailsBody to mark required fields as non-nullable In the server owner select, fixed the selected user disappearing if it was not in the search results In the allocation selects: Made the UI use alias:port when an alias is available, otherwise will default to ip:port Moved the ip:port text to a variable to avoid writing the same thing twice
Created the server startup tab Changed the way Container components are spaced in the Build Configuration tab and create server popup In the create server popup disabled the docker image select when only 1 image is available
Created the admin server databases tab (it only supports resetting the password and deleting the databases because for creating a database i need the hosts IDs, which there seem to be no API endpoint to get them) Added a modifier param to the Container component In the client server screen added a param to directly switch to the databases tab (this is used when the user goes from the admin server screen to the client server screen) Fixed “Missing Server ID” notification when client server is opened with databases tab as first tab
Created server manage tab (only reinstall & suspend/unsuspend cuz there seem to be no API endpoint to toggle install status & transfer) Created server delete tab Added 2 new button types, SUCCESS (green) and WARNING (yellow) In the admin server screen now most tabs are hidden whent he server is installing
Created users & user screen Updated the AGP (Android Gradle Plugin) Table headers now properly support multi line text (previously if one text was multiline while everything else wasn’t, all the divider would have a gap on the bottom except for the multiline one) In the client server screen, settings tab the UUID is now aligned to the right Improved screen change when deleting a server/node/user/location (instead of navController.popBackStack() (which basically acts as the back button) i specifically navigate to generic screen (eg. “user” -> “users”) and remove current screen from the back history so in the case that there is no screen to back into (eg if the app is opened via a deep link or stuff like that) it will go to the generic tab anyway) Added pull to refresh in the location screen
Created nests, nest & nest egg screens For nest egg screen, created 3 tabs: Configuration Variables Install Script it was pretty easy and quick to create those cuz they’re read-only as there’s no API endpoint to edit/delete them Added a back handler in the tabbed screens so that back button now goes to the previous tab instead of the entire previous screen In the client servers screen added a switch to see others’ server (admin only) Fixed capitalization in the getNestEgg function ( eggid -> eggId )
Added optional biometric authentication so people can lock their app behind phone code or biometric verification (like fingerprint etc.)
Added updated checking For F-Droid flavor it doesn’t do anything For APK flavor it checks my git repo For Play Store flavor it uses play store com.google.android.play:app-update library Updated the app site, available at https://ptero.stefdp.com Updated the changelog & app images in the fastlane folder Added an animation when switching between loading states with circular loading
Ships:
For rating & testing the project, please first read all of the text in the release. I made an Android app to manage your self-hosted Pterodactyl Panel instance (Currently only the client side, later I’ll also add the admin side). For who doesn’t know, Pterodactyl Panel is a panel usually used to manage game servers, but it can run anything, each server is just a docker container. The most challenging part was prob downloading files, and it still confuses me tbh because on the Android Studio emulator it works fine, but when if I try it on my actual phone (Samsung A55 5G) it randomly stops after a few seconds and I can’t seem to understand why...
|
1792810420.882077
|
1792896820.882077
|
|
44107
|
Pitch by @mincych | Stardance
|
mincych
|
0
|
157.54
|
0
|
[]
|
[{"post_id": 80080, "ts": "202 [{"post_id": 80080, "ts": "2026-10-16T05:40:23Z", "hours": 9.7833, "text": "pure hell of trial and error I am actually unsure if this will even work properly now. But fear not freinds, “BECAUSE I AM HERE” mha reference btw. I will actually take it a bit slow now, and ship in 10 days. Promise, no more delays."}, {"post_id": 79144, "ts": "2026-10-12T20:04:48Z", "hours": 9.1331, "text": "bug fixing session, no exciting updates, the more i think im coming close oto finishing it, more the work remains."}, {"post_id": 75106, "ts": "2026-09-29T04:40:22Z", "hours": 10.2189, "text": "Progres Report Simulated a hand from data in 2d space X axis is found from TDOA cross-correlation Y axis is found from doppler shift and EMA decay integration. -It’s not the best, but it works 70% of the time. The project is almost finished, but i’m not happy with the accuracy, so ill work on making it better. As good as it can be."}, {"post_id": 74329, "ts": "2026-09-27T05:38:08Z", "hours": 11.3422, "text": "#Code is not working. After the last post, I made some changes, and maybe messed up. Now something is wrong and im not able to figure it out. Since no AI is used in this project, this is becoming really hard."}, {"post_id": 73313, "ts": "2026-09-24T20:41:58Z", "hours": 13.1786, "text": "Progress Update Tried moving hand towards and away from laptop at known velocities. Tried to receive doppler shifted audio from the movement of hand. Extracted frequency shift from each tone independently using FFT. Did some more math and started losing my brain because it wasn’t working at the start. Now i’ll fuse methods, and get x and y coordinates for the plane."}, {"post_id": 72339, "ts": "2026-09-22T18:15:44Z", "hours": 6.7961, "text": "#Progress so far played a short pulse first from left speaker alone, then right one. Measured the angles of arrival, then calculated distance of how far apart these speaker and microphones are. This system can be used for any laptop, allowing for a general approach to be taken further. Yea next ill just verify two tone doppler separation."}, {"post_id": 71901, "ts": "2026-09-21T18:54:05Z", "hours": 7.9728, "text": "JUST WAIT FOR THE PROGRESS REPORT A BIT MORE, IM HAVING TROUBLE WITH THIS LAPTOP SPEAKER SEPERATION DETECTION. WHY IS THIS STUFF SO HARD"}, {"post_id": 71407, "ts": "2026-09-20T19:22:26Z", "hours": 15.3811, "text": "I was in the middle of making the distance calculator. So i’ll complete it and post my next devpost explaining it, this time limit of 10h is kinda annoying tbh. lol"}, {"post_id": 70197, "ts": "2026-09-18T18:00:14Z", "hours": 11.1686, "text": "Tried to merge FFT and doppler shift implementations finally Yea….. This is the only thing done in past 10 hours of work, but lemme explain how hard it is. This will be a weird analogy but hear me out. Imagine a robot, it can use sonar to keep a map of things around it and use the change in reflections and finally knows its own change in position. It’s absolute for knowing the surroundings around you, but it drifts overtime. Meanwhile a simple tracker using wheel can tell you change in location. But you can’t get information about surroundings. So they both are merged so that the drifting of sonar is stopped. I’m applying doppler shift in the same way, since it detects only the change, we can use it to correct the positions acquired by FFT implementation. Hope you all get it, ofcourse its not correctly working right now, so i’ll work on it."}, {"post_id": 68475, "ts": "2026-09-15T05:27:53Z", "hours": 21.5444, "text": "Updates : Heres another fun photo actually Actually tested with multiple laptops to see how much differently the same signals behave. A little bit different yes. Tested with 2 other ways just in case they were better. No and kinda?? 2/3 FFT implementation done Yes i am doing that doppler shift thing too now"}, {"post_id": 65776, "ts": "2026-09-11T04:26:53Z", "hours": 8.3614, "text": "Hardware obstacle faced btw heres a cool monster can gun i made in my free time ive been testing with sounds all this time. But i didnt realise that different laptops have different sound systems. Like downwards firing speakers and upwards firing speakers. So i have decided that this wont be able to be made for all laptops. So i definitely will have to create a video for this. lol. Im on fire!"}, {"post_id": 64028, "ts": "2026-09-08T04:24:03Z", "hours": 9.3717, "text": "THIS IS HELL I have nothing new to show, since im still working on the same thing for 2 devposts now. So heres a cool photo Anyway, after successfully implementing this FFT, I’ll move on to integrate doppler shift as well, like those robots that although have a lidar, still have that internal tracking system. Since lidar drifts, the internal tracking system corrects it. Atleast thats the vision. And implementing doppler shift isnt easy AT ALL, atleast for left to right motion. im actually tired of failing and trying some solution without using AI. It would have made it a lot easier but, I’m kinda stupid and egoistic ig?"}, {"post_id": 63139, "ts": "2026-09-06T19:33:16Z", "hours": 9.9097, "text": "Made significant progress on FFT implementation I’m maybe 1/3rd the way to implement FFT ig? This is getting pretty hard. I’m spending most of the time just writing, running and testing the code of this implementation. As it turns out laptops arent really the best at this stuff. And stuff like this being done by actual companies, makes sense now. It’s way too difficult."}, {"post_id": 53968, "ts": "2026-08-24T17:57:15Z", "hours": 6.7728, "text": "Worked more on the implementation of FFT The picture attached is just when there is forward or backward movement of hand, it glows brighter. Cus I tested with doppler shift a little. So, I learnt a few more concepts required to make this. Applied FFT a bit more nicely and neatly. ALSO I LEARNT COMPLEX FFT WHICH IS AMAZING TBH. Yea, so far its just applying what I’m learning. Not easy for a 16 year old to do college shit."}, {"post_id": 51177, "ts": "2026-08-20T17:57:49Z", "hours": 6.6025, "text": "Implemented a basic distance detector using laptop speaker and microphones Tbh all these difficult concepts, you understand them, do their maths, and then boom, you just implement it using a library. WHY DID I SPEND SO MUCH TIME UNDERSTANDING THEM IN THE FIRST PLACE. I AM A 16 YEAR OLD, ITS REALLY HARD FOR ME TO UNDERSTAND COLLEGE LEVEL SIGNAL PROCESSING, I STILL HAVE FFT AND TO UNDERSTAND!!!! anyway I’ve been working on a project that basically sees without seeing. So far, i’ve understood concepts like convolution, sampling rate, correlation, stuff to basically find a sound we want from a sample. I then wrote a code to send out a linear increasing list of frequencies from 10000 to 15000. Then do a convolution of the recieved sound with the target sample to detect where in the recieved sound lies the target sample."}]...
|
Title: Pitch by @mincych | Stardance
Devlogs:
Imp Title: Pitch by @mincych | Stardance
Devlogs:
Implemented a basic distance detector using laptop speaker and microphones Tbh all these difficult concepts, you understand them, do their maths, and then boom, you just implement it using a library. WHY DID I SPEND SO MUCH TIME UNDERSTANDING THEM IN THE FIRST PLACE. I AM A 16 YEAR OLD, ITS REALLY HARD FOR ME TO UNDERSTAND COLLEGE LEVEL SIGNAL PROCESSING, I STILL HAVE FFT AND TO UNDERSTAND!!!! anyway I’ve been working on a project that basically sees without seeing. So far, i’ve understood concepts like convolution, sampling rate, correlation, stuff to basically find a sound we want from a sample. I then wrote a code to send out a linear increasing list of frequencies from 10000 to 15000. Then do a convolution of the recieved sound with the target sample to detect where in the recieved sound lies the target sample.
Worked more on the implementation of FFT The picture attached is just when there is forward or backward movement of hand, it glows brighter. Cus I tested with doppler shift a little. So, I learnt a few more concepts required to make this. Applied FFT a bit more nicely and neatly. ALSO I LEARNT COMPLEX FFT WHICH IS AMAZING TBH. Yea, so far its just applying what I’m learning. Not easy for a 16 year old to do college shit.
Made significant progress on FFT implementation I’m maybe 1/3rd the way to implement FFT ig? This is getting pretty hard. I’m spending most of the time just writing, running and testing the code of this implementation. As it turns out laptops arent really the best at this stuff. And stuff like this being done by actual companies, makes sense now. It’s way too difficult.
THIS IS HELL I have nothing new to show, since im still working on the same thing for 2 devposts now. So heres a cool photo Anyway, after successfully implementing this FFT, I’ll move on to integrate doppler shift as well, like those robots that although have a lidar, still have that internal tracking system. Since lidar drifts, the internal tracking system corrects it. Atleast thats the vision. And implementing doppler shift isnt easy AT ALL, atleast for left to right motion. im actually tired of failing and trying some solution without using AI. It would have made it a lot easier but, I’m kinda stupid and egoistic ig?
Hardware obstacle faced btw heres a cool monster can gun i made in my free time ive been testing with sounds all this time. But i didnt realise that different laptops have different sound systems. Like downwards firing speakers and upwards firing speakers. So i have decided that this wont be able to be made for all laptops. So i definitely will have to create a video for this. lol. Im on fire!
Updates : Heres another fun photo actually Actually tested with multiple laptops to see how much differently the same signals behave. A little bit different yes. Tested with 2 other ways just in case they were better. No and kinda?? 2/3 FFT implementation done Yes i am doing that doppler shift thing too now
Tried to merge FFT and doppler shift implementations finally Yea….. This is the only thing done in past 10 hours of work, but lemme explain how hard it is. This will be a weird analogy but hear me out. Imagine a robot, it can use sonar to keep a map of things around it and use the change in reflections and finally knows its own change in position. It’s absolute for knowing the surroundings around you, but it drifts overtime. Meanwhile a simple tracker using wheel can tell you change in location. But you can’t get information about surroundings. So they both are merged so that the drifting of sonar is stopped. I’m applying doppler shift in the same way, since it detects only the change, we can use it to correct the positions acquired by FFT implementation. Hope you all get it, ofcourse its not correctly working right now, so i’ll work on it.
I was in the middle of making the distance calculator. So i’ll complete it and post my next devpost explaining it, this time limit of 10h is kinda annoying tbh. lol
JUST WAIT FOR THE PROGRESS REPORT A BIT MORE, IM HAVING TROUBLE WITH THIS LAPTOP SPEAKER SEPERATION DETECTION. WHY IS THIS STUFF SO HARD
#Progress so far played a short pulse first from left speaker alone, then right one. Measured the angles of arrival, then calculated distance of how far apart these speaker and microphones are. This system can be used for any laptop, allowing for a general approach to be taken further. Yea next ill just verify two tone doppler separation.
Progress Update Tried moving hand towards and away from laptop at known velocities. Tried to receive doppler shifted audio from the movement of hand. Extracted frequency shift from each tone independently using FFT. Did some more math and started losing my brain because it wasn’t working at the start. Now i’ll fuse methods, and get x and y coordinates for the plane.
#Code is not working. After the last post, I made some changes, and maybe messed up. Now something is wrong and im not able to figure it out. Since no AI is used in this project, this is becoming really hard.
Progres Report Simulated a hand from data in 2d space X axis is found from TDOA cross-correlation Y axis is found from doppler shift and EMA decay integration. -It’s not the best, but it works 70% of the time. The project is almost finished, but i’m not happy with the accuracy, so ill work on making it better. As good as it can be.
bug fixing session, no exciting updates, the more i think im coming close oto finishing it, more the work remains.
pure hell of trial and error I am actually unsure if this will even work properly now. But fear not freinds, “BECAUSE I AM HERE” mha reference btw. I will actually take it a bit slow now, and ship in 10 days. Promise, no more delays....
|
1792572841.2805002
|
1793177641.2805002
|
|
55
|
Vestige by @Rupnil | Stardance
|
Rupnil
|
0
|
157.82
|
0
|
[]
|
[{"post_id": 54465, "ts": "202 [{"post_id": 54465, "ts": "2026-08-25T14:08:25Z", "hours": 9.6325, "text": "(v0.5.4-beta) VESTIGES ARE ALIVE! Changelog: (v0.5.4-beta) Vestige 7 Animation : Les goo! another task checked off from the todo list! Completed all the animations of the vestiges that forms the backbone of the game- This was the final one (on the path). Next lwk i think ill do sound design maybe Lapsed (v0.5.3-beta) Vestige 6 Animation : I got stuck in this session cuz of a simple bug :skul what happened was that the animation player in the wheelchair scene was changing the position of the root and so it messed everything up- Took me a while to solve it lol. Added some movement and wheel rotation. Lapsed (v0.5.2-beta) Vestiges 3,4,5 Animation : I thought i would complete making the animations today but clearly I failed- Oh well I fixed a LOT of things and FULY completete the third forth and fifth vestiges Lapsed (v0.5.1-beta) Vestiges 1,2,3 Animation : Since I completed modelling and texturing the vestiges the only logical next step is to incorporate them into the game, and thats what ive been doing! Integrated the first 3 vestiges and added some simple animations associated w em Lapsed (v0.4.14-beta) Vestige 4,5,6,7 Texturing : Oh my god- Finished all ALL the texturing- I really wanted thisto be finished faster lol. Anyways in the next session I would make the animations and placements of the vestiges! Lapsed (v0.4.13-beta) Vestige 1,2,3 Texturing : Completed texturing vestiges 1, 2 and 3- i.e. the crib the swing and the tent. Were a lot of fun to see the white models come to life lol. I really like how they are turning out Lapsed (v0.4.12-beta) Models Finalizing : Last day I completed modelling all the vestiges and now only the texturing is left. In this session I finalized the models and meshes and next ill texture them! Lapsed Next Step(s): Vestiges Vestiges animations Watch Tower Furniture Sound Effects Notes: Yippie I completed all the vestiges progression and their associated animations! Idk what ill work on next but maybe sound design? idrk tho"}, {"post_id": 50528, "ts": "2026-08-19T18:30:38Z", "hours": 9.7806, "text": "(v0.4.11-beta) Lantern, & VESTIGESSS Changelog: (v0.4.11-beta) Vestige 7 Model) : FINALLY FIALTLTJkdas I completed all the vestiges! The final one was the mirror and the chair. Im so tried cya Lapsed (v0.4.10-beta) Vestige 6 Model : Made another model– the wheel chair i am trying to complete this first so ill add the more niche things like umbrellas later Lapsed (v0.4.9-beta) Vestige 5 Model : Okay fifth one down! TWO MOREW i repeat 2 MORE- i wanna complete them asap but lets see! This was a pretty challenging one dawg- the piano was insane BUT it looks so good now Lapsed (v0.4.8-beta) Vestige 4 Model : Made the fourth model– the table, chair, cups and the candles! next up is the musical item- idk how i will make the piano but oh well lets see! Lapsed (v0.4.7-beta) Vestige 3 Model (1) : Erm this is the 3rd model that im doing- ik the uv will be fked uop but oh well. Made the tent, wooden logs and the camfire Lapsed (v0.4.6-beta) Vestige 2 Model : Oh god it is so late but yet im pushing it- I did like 30 more minutes im going to eep now- Anyways in this session i made the swing! Only model tho Lapsed (v0.4.5-beta) Vestige 1 Modelling : In this session I just modelled the crib and the teddy as fast as i could- I need to ramp up my speed here- I dont got much time Lapsed (v0.4.4-beta) Hanging Lanterns & Placement : Modelled the hanging lantern and placed it around the map especially the bunker porttions Lapsed (v0.4.3-beta) Lantern Model : In about 2-ish hours i cooked up this absolutely BEAUTIFUL and awesome lantern model. I just have no words– its just perfect! also added some particle effects and yeah! looks pretty cool Lapsed (v0.4.2-beta) Vestige: Pointer : A point at the center of the screen. yes Lapsed Next Step(s): Ending Bench interaction Bunker and Tunnel Models Watch Tower Furniture Vestiges Vestiges animations Notes: Im so so so close to completing this! so I need pick up the pace here! Completed all the vestiges here"}, {"post_id": 47607, "ts": "2026-08-15T16:42:37Z", "hours": 8.1083, "text": "(v0.4.1-beta) Ending, Credits n Modeling Changelog: (v0.4.1-beta) Bunker/Tunnel Model & Texturing) : Oh finally! I am finished with the alpha versions and i moved on to the beta stage! In this session I completed modelling and texturing the bunker as well as the tunnel models! Lapsed (v0.3.9-alpha) Placement, Playtesting & Exporting : Oh my! I am really finished with alpha versions! I will be now moving on to the beta stage. In this session I placed trees, playtested and exported for the alpha release. Lapsed (v0.3.8-alpha) Credits (3) : Yippie i completed the credit scene although the “press anmything to conitnue” doesnt work as of now– but ill add it on my todo for later! Lapsed (v0.3.7-alpha) Credits (2) : THe second lapse/commit of me workin on the credits! I am ALMOSt complete i js need one more session to finalize some stuff Lapsed (v0.3.6-alpha) Bug Fixes & Credits (1) : Fixed an annoying bug where the top of the watch tower was affected by the stair aread3d. Now an invisible wall prevents jumping. Also Added the basic Credits scene. Lapsed (v0.3.5-alpha) Bench Camera Animation, Interaction (2,3) : Aha! I think i am really done with the ending animations! Now the E to interact only works when u are looking directly at the bench. I think i might change the blinking animation tho- Lapsed (v0.3.4-alpha) Bench Camera Animation (1) : Right now I’m Adding the animation and controls for the bench interaction and the cut scene. Added some blinking thing and location changes and movement- Will make the cutscene next! Lapsed Next Step(s): Ending Bench interaction Bunker and Tunnel Models Watch Tower Furniture Vestiges Notes: Yesterday I released the alpha version! the 0.3.9, for win, mac as well as linux! I completed my first priority things and i have moved on to the beta!"}, {"post_id": 45302, "ts": "2026-08-11T17:45:17Z", "hours": 10.4367, "text": "(v0.3.3-alpha) Bunker & Interaction Changelog: (v0.3.3-alpha) Player Interaction (2) : Me add raycast to the face of player, poke poke thing, thing cry, raycat know what it did, raycast say name, i hear, i do something; In a nutshell the raycast detects the collider and if its the bench it enables the e to interact thing Lapsed (v0.3.2-alpha) Player Interaction (1) : Set up the basic of the player interaction– ill work on it more tmrw Lapsed (v0.3.1-alpha) Map Positioning (Ending) : Positioned the map so that the dead space is directly below the drop chute- and other fixes and tests Lapsed (v0.2.13-alpha) Bunker: More models & logics (7) : Improved model to include a turn and a drop, finally added more mechanics and animations. The bunker now dissapears if u look away. fixed some other logics Lapsed (v0.2.12-alpha) Bunker: Mechanics; Showing Maps (6) : When player enters the doors closes and then the original map dissapears, you keep walking forward and the secondary map is spawned. As u come out of the other side the tunnel leads you to the secondary map. Lapsed (v0.2.11-alpha) Bunker Prototype: Door Mechanics & Stairs (5) : made the door open and close animations actually happen with the players actions. Also added an AREA3D which allows the stair logic to kick in. Added the map thing but will animation and add the mechanics in the next update! Lapsed (v0.2.10-alpha) Bunker Prototype: Model & Animations (4) : experimented with another map and stuff. Modelled the top portion and added a simple animation for the doors Lapsed (v0.2.9-alpha) Bunker Prototype: Model (3) : completed the bunker modelling – mostly protype rn but yeah! Next up i think imma make the mechanism of the bunker mm Lapsed (v0.2.8-alpha) Weeping Silhouette: Animation (2) : Les goo i finally completed the weeping silhouette thing! Now it explodeds kinda when u go near it and leaves the player stranded they player has to navigate their way into the next parts! Lapsed (v0.2.7-alpha) Weeping Silhouette (1) : modelled the weeping silhouette and placed it where its supposed to- in the next update I’ll add the animations and stuff! Lapsed (v0.2.6-alpha) Bunker Prototype (1+2) : Well i tried making the bunker prototype but erm i didnt realise how tuff this is gonna be skull. I will try again cuz Im so sick to think straight- ill try to do some other thing toda Lapsed Next Step(s): Bunker prototype Ending Bench interaction Notes: Ive almost completed the FULL game sequence! like ik its using prototype things but the core fucntionality is almost done! super excited"}, {"post_id": 41473, "ts": "2026-08-05T18:12:00Z", "hours": 9.2175, "text": "(v0.2.5-alpha) Memory/Vestige Chains (Prototype) Changelog: (v0.2.5-alpha) Vestige Chain (4) : Les goo i completed the full chain of vestiges currently they are just cubes but i will def make models for them later! Lapsed (v0.2.4-alpha) Placement + Vestige Chain (3) : Ok i placed all of the vestiges and currently made the animation till the Vestige 4 – 4 moire to go (counting the watchtower). Idk i think i did some other stuff that i prolly forgot lmfao. Lapsed (v0.2.3-alpha) Particle Fix + Vestige Chain (2) : So previously there was this thing that the particles were all dark and glomy cuz of the exposure thing- Fixed that in this lapse/commit. Also i added the 2nd vestige prototype! Lapsed (v0.2.2-alpha) Vestige Chain: Starting (1) : Yay so i started the next priority in the todo list! I started the vestige chain using only a cube lmao! i will model them later – this is the prototype phase Lapsed (v0.2.1-alpha) Vestiges brain storming : So previously there was this thing that the particles were all dark and glomy cuz of the exposure thing- Fixed that in this lapse/commit. Also i added the 2nd vestige prototype! (v0.1.6.3-alpha) Todo Cleanup : Yep so the thing was that I was too disorganized and too overwhelmed. I sat down today and wrote down everything I think i have a clear picture of what I want to do now! (v0.1.6.2-alpha) Rename & Bunker Prototype + Optimization : I renamed everything. I think i said yesterday that The story is now one single cohesive storyline so I renamed everything to match the new thing. Also I made a hole in the ground (for the bunker) and added a mesh just to visualize it lol. Lapsed (v0.1.6.1-alpha) Story + Optimizations : Right so as i mentioned yesterday i had finalized the plot, in this commit/lapse I wrote it down! Oh also I did something that Ive kept in my todo for a long time lol – I optimized the flower and grass models Next Step(s): Rewriting the todos & story Prototype Vestige Chain Bunker prototype Notes: Wow im speeding up here! i actually finished the entire vestiges sequence Next up imma prototype the bunker"}, {"post_id": 39581, "ts": "2026-08-02T17:16:03Z", "hours": 6.3994, "text": "(v0.1.5-alpha) Better Movement, Stealth & Environment! Changelog: (v0.1.5-alpha) Environment Rework & Jitter Fix : Since i completed the lore/plot finally trust me (not written but thought of) I needed to change the environement a bit kjust to match the vibe i am going for now! it is now a whitish dusk and not pitch black. ALso i think i fixed the jitter thing. Lapsed (v0.1.4(J)-alpha) Player Movement Rework: Crouching (8) : I have completed reworking the player movement for now– next up ill actually focus ont he core gameplay. In this comit/lapse i have added a crouch functionaility and fixed some other logical stuff that im not going deep into Lapsed (v0.1.4(I)-alpha) Player Movement Rework: Area3D (7) : OKsy! so i and @seb figured out a way to fix the issue i talked about in the previous commit/lapse/. The fix was pretty neat! i added an area 3d on the stairs and the smoothing only work iin this area! i also turned off jumping here Lapsed (v0.1.4(H)-alpha) Player Movement Rework: Smoothness (6) : Fixed the erm “smoothing problem” Previously it was hella snappy and awful to look at - now I added a smoothing thing in the _process but uhhh another problem now – the jumping feels floaty and walk is smoothed out :/ Lapsed Next Step(s): Watch Tower Model Watch Tower Texture Auto Jump on Stairs Core Gameplay Rewriting the todos & story (final istg) Notes: Finally thought of a cohesive plot. I do not have much time or energy to make another chapter – so ive decided to merge everything into one gameplay – the prologue. So yeah! we’ll see how it turns out (i have high hopes tho)"}, {"post_id": 38338, "ts": "2026-07-31T17:47:47Z", "hours": 5.8597, "text": "(v0.1.4(G)-alpha) Moving & Jumping on the Stairs (Passed!) Changelog: (v0.1.4(G)-alpha) Player Movement Rework: Auto-jump (5) : Ok i kinda cheesed it ngl- I was literally being ragebaited because it was NOT working any how. I am using a not so beautiful method but hey it works and thats what counts – ill polish it later dw. Lapsed (v0.1.4(F)-alpha) Player Movement Rework: ?? (4) : wtf? what happened? what did i do? this is insane i really dont know………. soo erm climbing stairs doesnt work and there are some pretty bad bugs like being able to fly- lwk i might revert this session Lapsed (v0.1.4(E)-alpha) Player Movement Rework: Noclip (3) : added a pretty peak thing. Ok so added a noclip feature – this is like the spectator mode in minecraft lmfao. Ive been meant to do this for a long time but here i am! finally added this – this will help me a ton i js know it lol Lapsed (v0.1.4(D)-alpha) Player Movement Rework (2) : Yoo so added a bunch of new things such as air friction, acceleration deceleration and more. I am having one tiny bug tho – that is i cannot turn when jumping but dw ill figure out a way to fix it! Also if u forgot Im doing the player rework just to make the autojump thing Lapsed (v0.1.4(C)-alpha) Player Movement Rework (1) : Okay So i realised it wwill be extremely difficult for me to figure it out on my own, and even the big ones ( @seb ) had no freaking idea lmao. SO what i am doing bnow is following a player/fps controller tutorial where they add this autojump feature :3 Lapsed Next Step(s): Watch Tower Model Watch Tower Texture Auto Jump on Stairs (Done but polishing left) Interior Design of the Watch Tower Core Gameplay Notes: These sessions were lwk ragebait – i will NOT talk about the 4th session cuz i have no freaking idea about it. I found a weird way and workaround to do the auto jump. Although it might not be the most beautiful but it works and thats what counts"}, {"post_id": 37110, "ts": "2026-07-29T16:22:46Z", "hours": 5.7036, "text": "(v0.1.4(B)-alpha) Walking on the Watch Tower (Failed) Changelog: (v0.1.4(B)-alpha) Walking on Stairs (2) : Lwk I just messed around for the whole time I literally do not know wht to do at this point I will revisit this tommorow (I dmed my fwend Seb lmao). I tried many methods but i couldnt seem to be able to make this work tho Lapsed (v0.1.4(A)-alpha) Walking on Stairs (1) : Yo gang I tried my to do the stairs walking thing but as you would expect I completely failed lmfAaooo. Uh I really dont know how to move next but ill try my best Lapsed (v0.1.3-alpha) Mountains Texture Fix : So erm as i completed the watch tower im moving on to other thigns– first i saw that this was in my todo for a while so fixed it lol Lapsed (v0.1.2(K)-alpha) Watch Tower Texturing (4.4) : Shalalala sha boinky! I actually finished the (skeleton) of the watch tower model! Omg i dont believe it! I am feeling a large weight off my shoulders lmfao! But hey its not done yet! I have so so so sos os so much more to do- next up ill fix the stair walking and oh a story redesign! Lapsed (v0.1.2(J)-alpha) Watch Tower Texturing (4.3) : Finally completed texturing the stairs – the railings and the beams are still remaining but ill try to do them soon enough 👍 Lapsed (v0.1.2(I)-alpha) Watch Tower Texturing (4.2) : oh my GOD BRO this is so painful i really hope i could do something else to speed this up but yeah idk- So nothing other than UV unwrap stuff NOT EVEN ONE mesh and i have 2 more to go- I am dying :D Lapsed Next Step(s): Watch Tower Model Watch Tower Texture Auto Jump on Stairs Interior Design of the Watch Tower Notes: Gang I tried my best I literally cannot seem to be able to be able to find a way to make the auto jump thing I need to call the big guns now! ( @seb ) Anyways, I obv completed the watch tower texturing"}, {"post_id": 35856, "ts": "2026-07-27T15:49:43Z", "hours": 7.2233, "text": "(v0.1.2(H)-alpha) Watch Tower Texturing (90%) Changelog: (v0.1.2(H)-alpha) Watch Tower Texturing (4.1) : Okay so nothing but I joined a hell lot of meshes and objects- i mean the stairs portion. And then unwrapped the UV this took me some time but i finished it! next imma fix the UV and then COMPLETE this FOR ONCE AND ALL! Lapsed (v0.1.2(G)-alpha) Watch Tower Texturing (3.4) : Ayy so i completed the Leg section of the watch tower! i agree most of the work is uv unwrapping because of the faces and all yk. But yeah! im making progress only the stairs section left ig!!!! Lapsed (v0.1.2(F)-alpha) Watch Tower Texturing (3.3) : Teeny tiny lapse! just combined and unwrapped the support beams- ill prolly texture it tommorw and then move on to the stairs section Lapsed (v0.1.2(E)-alpha) Watch Tower Texturing (3.1+3.2) : Holy smokes! What a productive day! got so so so much done today – So I completed the Legs, and support beams in this lapse so only the cross supports are left! Also blender crashed like 300 times today– i really need the macbook gng i got the signs already! Lapsed (v0.1.2(D)-alpha) Watch Tower Texturing (2) : Oh another milestone i suppose! I completed the UV unwrapping of the fence and also textured it! I dont have any more stuff to talk about gng im so tired and busy nowadays Lapsed —## Next Step(s): Watch Tower Model Watch Tower Texture (WIP GNG ALMOST) Notes:OK LISTEN! LISTEN I swore to @Keyboard that I wont devlog until I have completed the whole texturing BUT its getting too ridiculous not to- im alr at 7 hours and yeah. Also big ass devlog-"}, {"post_id": 33972, "ts": "2026-07-24T10:35:59Z", "hours": 4.6819, "text": "(v0.1.2(C)-alpha) Watch Tower Texturing (45%) Changelog: (v0.1.2(A)-alpha) Watch Tower Texturing (1.5) : Yay so i made the Floor section completely- the floor and the uhh supprts ig lol. THis was fun Ill work more tmrw Lapsed (v0.1.2(A)-alpha) Watch Tower Texturing (1.2 + 1.3 + 1.4) : 1. So nothing much to brag about here- I completed texturing the roof. It was pretty easy ngl - anyways I’ll do the UV unwrapping of the walls next ig. 2. So I unwrapped the UV layout of the walls and its Horrible! It will take so much time to manually fix everything lmao. I did like 50% of the unwrapping today NOT THE TEXTURNG BTW! so we will see tmrw! gn. 3. Completed the UV unwrapping of the walls and also the texturing of the walls! the room section of the texturing is done! moving on to the floor and stairs! Lapsed Next Step(s): Watch Tower Model Watch Tower Texture (WIP) Notes: Aww man i couldnt really focus on this project for the last few days i had so much work! had 2 assignments- submitted them today so i think i can focus in more today!!"}, {"post_id": 32257, "ts": "2026-07-21T09:49:57Z", "hours": 3.5792, "text": "(v0.1.2(A)-alpha) Watch Tower Texturing (2%) Changelog: (v0.1.2(A)-alpha) Watch Tower Texturing (1.1) : Yay so i moved on to the texturing part of the watch toweeeeerr. Just made the door for now but I will accelerate the work from tmrw trust Lapsed (v0.1.1(Q)-alpha) Watch Tower Modelling (11) : oh my gosh! im done with the modelling! well i mean i may add the window margins stuff later but yeah! i am now starting the texturing! i made the glass and experimented with some stuff Lapsed (v0.1.1(P)-alpha) Watch Tower Modelling (10.4) : ok i am being for REAL right now. and u HAVe to believe me when i say that i am FInALLY COMPLETE with the freaking fencing stuff i am NOT doing that again 💀 ok now the only thing left is the window panes and THEn i can finally move on to the texture- Lapsed Next Step(s): Watch Tower Model Watch Tower Texture (WIP) Notes: Yooooo so I finally completed the modelling of the watch tower and started texturing it recently. I have some work to do today so idk how long i can code- Ill try my best tho!"}, {"post_id": 31170, "ts": "2026-07-19T10:42:47Z", "hours": 3.8003, "text": "(v0.1.1(O)-alpha) Watch Tower Modelling (90%)! Changelog: (v0.1.1(O)-alpha) Watch Tower Modelling (10.3) : hell yeah! completed the guard rails– now uhh some more fences are remaining i promise it wil be done very soon im trying my bestttt Lapsed (v0.1.1(N)-alpha) Watch Tower Modelling (10.2) : Made another layer of guard rails beside the stairs - yeah just that 💀 i have no more words to write here Lapsed (v0.1.1(M)-alpha) Watch Tower Modelling (10.1) : I might work again later after coming back from art classes- but im not sure as i have some tuition work to do :( I also have some pending ssignments! yikes!!! anyways here’s what i did: Made the rails of the stairs with some bars - first and second stairs. did some desmos stuff- Lapsed Next Step(s): Watch Tower Model (WIP) Watch Tower Texture Notes: ok i dont really know what to write hereeee nowwww- maybe the next time"}, {"post_id": 30545, "ts": "2026-07-18T06:28:52Z", "hours": 5.045, "text": "(v0.1.1(L)-alpha) Watch Tower Modelling (75%)! Changelog: (v0.1.1(L)-alpha) Watch Tower Modelling (9.3) : Joined a lot of meshes together for efficiency and ease of use. Oh also made the raining v1 for the first layer of the stairs. I have my prize distribution and investiture ceremony so yeah! see ya laterrrrr Lapsed (v0.1.1(K)-alpha) Watch Tower Modelling (9.1 + 9.2) : 1. omg omg omg! stairs in progressssss! Made two flights of stairs- what i mean is i made one flight of stairs and then copied it 💀 Anyways I’m almost done with the modelling i think- The stairs and the windows are the only things are left (oh maybe more optimization :p) 2. Added much More stairs and the topmost layer. I will make the integration of the stairs to the floor tommorow, ALSO i will add the railings and all tmrw I am sleepy as frick! Lapsed (v0.1.1(J)-alpha) Watch Tower Modelling (8) : Wow! what a productive session! Ok so a lot of thing was done. Here’s what i did in short: The legs of the watch tower was made before but in this session i added the supports and support beams! i again followed the model as an inspiration and made the supports! Looks prety cool! Lapsed — Next Step(s): Watch Tower Model (WIP) Watch Tower Texture Notes: um holy shit i am doing a 4? or 5 hour devlog 💀 this is unreal! Anyways this is draggin me sooo muchh but i think im super close to finishing this"}, {"post_id": 29396, "ts": "2026-07-16T10:12:50Z", "hours": 3.5758, "text": "(v0.1.1(I)-alpha) Watch Tower Modelling (60%)! Changelog: (v0.1.1(I)-alpha) Watch Tower Modelling (7) : Nothing much just edited the fences a bit and resized them in order to prevent overlapping. Oh also I made the roof – needs polishing ik but its a start lmao- i want to complete the modelling as soon as possible lwk- Lapsed (v0.1.1(H)-alpha) Watch Tower Modelling (6) : Yoo moving from vertices selection to something different felt awesome! anyways i made the fence? i think thats what its called- On all four sides. I am following a model for inspiration and im pretty happy on how it is turning out! Lapsed (v0.1.1(G)-alpha) Watch Tower Modelling (5.3) : FINALLY FINALLY! I have completed optimizing the walls of the watch tower. What i mean is I reduced the number of vertices and faces in the model. This will help me immensely while texturing the UV. I created another backup just in case lmao Lapsed (v0.1.1(F)-alpha) Watch Tower Modelling (5.1 + 5.2) : I tried cleaning up the Watch Tower model but idk how it all got messed up anyways I will try again ONE LAST TIME tommorow if it works it works otherwise i have to roll back to the backup and just forget about cleaning up- i messed up yeah Lapsed Next Step(s): Watch Tower Model (WIP) Watch Tower Texture Notes: i NEED to finish modelling this ASAP gng 💀 wish me luck ill be lockin in today 🔥"}, {"post_id": 28297, "ts": "2026-07-14T13:43:26Z", "hours": 3.3656, "text": "(v0.1.1(E)-alpha) Watch Tower (40%)! Changelog: (v0.1.1(E)-alpha) Watch Tower Modelling (4) : erm so nothing productive just me fixing the HUMONGOUS number of vertices that have been created for god knows what. Lmfao i messed up at the end and i have to go back to the last backup Oh well i have tuitions now will do more stuff later Lapsed (v0.1.1(D)-alpha) Watch Tower Modelling (3) : Ah yes, guess why there is no lapse for this update- thats right! Lookout messed up and I cannot recover the lost footage and publish it in lapse. I lwk forgot what i did in this update 💀 You are gonna guess it (v0.1.1(B&C)-alpha) Watch Tower Modelling (1&2) : Guess who forgot to commit THATS RIGHT ITS ME- In this lapse I made the skeleton of the watch tower that is going to be the main attraction of the prologue. I do not have much time and hence i am ending this session prematurely! See you tmrw! More modelling of the watch tower, made the top thing the uhh room? ig tested the model size with the player size in godot and i found that it was a bit small so i fixed that! Made some windows for the watchtower so that everything is visible Lapsed Next Step(s): Watch Tower Model (WIP) Walking animation Story Redesign Improved Silhouette Mechanics — Notes: Ah yes I lost like 30 minutes of lapse yesterdat cuz lookout servers died oh welp! here i am again!"}, {"post_id": 27139, "ts": "2026-07-12T12:55:30Z", "hours": 3.9608, "text": "(v0.1.1(A)-alpha) Map Redesign and Improved mechanics! Changelog: (v0.1.1(A)-alpha) Improved Silhouette Mechanics : oh my! it looks so sick! il definitely play test this once more but heres what i did in brief: Previously there used to be different silhouttes but NOW theres only one that “teleports” when u reach it as if guiding you somewhere – to the watchtower :3 Lapsed (v0.0.10(E)-alpha) Map Redesigning (4) : I think i am done with the forest/map layout for now im pretty happy! plugged off some paths that i didnt want the player to go through- removed a ton of legacy code. Next up i think ill work on the silhouette mechanics which i cooked up in the new narrative! wish me luck! Lapsed (v0.0.10(D)-alpha) Story + Map Redesigning (3) : Again one of the shortest lapses i have made but its enough for me to get the 1 hour streak lmao- I promise Ill work honestly from tmrw, Exams finished just today and im exhausted asf- Please excuse me! Made updates to the story too Next Step(s): Watch Tower Model Walking animation Story Redesign Improved Silhouette Mechanics Notes: Holy moly! the silhouette (YES SINGULAR) works wonderfully now! its like its guiding you! im really happy with the result i think ill model the watch tower."}, {"post_id": 26446, "ts": "2026-07-11T07:48:49Z", "hours": 1.8575, "text": "(v0.0.10(C)-alpha) Story and Map Redesign Changelog: (v0.0.10(C)-alpha) Story + Map Redesigning (2) : So as i am changing the narrative i need to update the map accordingly, A very small update because my mouse stopped wrking but oh well i just changed some textures and added trees. Also making a story narrative in 2nd person ig? Lapsed (v0.0.10(B)-alpha) Story Redesign (1) : Brainstormed a lot of stuff- and rewrote the prologue storyline- the old one was pretty basic i like the new one more and I mostly changed it because i am thinking of the next chapter too and i want a seamless transition- just hang on with me ill cook something truly epic :3 Next Step(s): Boulders / Rocks (WIP) Walking animation Story Redesign (WIP) Notes: OKay today ill lock tf in my exams just finished!"}, {"post_id": 25183, "ts": "2026-07-09T07:50:16Z", "hours": 2.7572, "text": "(v0.0.10(A)-alpha) Walk and Map Redesign Changelog: (v0.0.10(A)-alpha) Map Redesigning (1) : I think im going to go on a different direction with the prologue scene! i changed the pathways because i think it was too complex and looping- I am also thinking of turning the log cabin into an watch tower for more ominous feel! im working on the story hopefully i can figure it out! Lapsed (v0.0.9(F)-alpha) Walk Animation (Camera Shake) : Yoooo! holy smokes this look epic spent a good chunk figuring out how to make the walk animation turns out its easier to do it through code- thats why i ended the lapse prematurely and worked on it in Rider IDE! it look so cool rn it has head bobbing in all three dimensions! Lapsed Next Step(s): Boulders / Rocks (WIP) Walking animation Story Redesign Notes: I had some different ideas about the story and im working on it- redesigned the map a bit! i think ill do more stuff"}, {"post_id": 24010, "ts": "2026-07-07T09:18:07Z", "hours": 3.4614, "text": "(v0.0.9(E)-alpha) More erm Animations! Changelog: (v0.0.9(E)-alpha) Silhouette Spawning, Flickering Animations (2) : Yay i finally made the silhouette spawning animation! it looks sick! the way it slowly comes into view- really cool i also did a bunch of things here and there that im not going into detail :3 Lapsed (v0.0.9(D)-alpha) Silhouette Spawning (1) : Erm uhh idk- i experimented with both script and animationplayer animations but i couldnt seem to get them to work - the script seemed to fail completely while the animation player got me some success but not enough- I will look in to this tmrw! Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations Complete Death animation Walking animation Notes: yoo gng i had my regional language and physics exam today and i lwk think i did pretty well! Anyways i did some work and i think ill do more now!"}, {"post_id": 23334, "ts": "2026-07-06T07:01:47Z", "hours": 2.5928, "text": "(v0.0.9(B)-alpha) Animations! Changelog: (v0.0.9(B)-alpha) Waking up & Counter Animation : So added the waking up animation - this was a new thing for me that i think i leanrt! the animation player is a really cool feature! Also now the silhouette counter shows up only after the first silhouette is found Lapsed (v0.0.9(A)-alpha) Death Animation : Fixed a bug (ig? idk if u can call it that) that made it so whenever u looked away from the silhouette the animation would completely freeze. Turns out there was a VisibleOnScreenEnabler3D child which froze the process- Its fixed now! Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations Complete Death animation Walking animation Notes: uhh i dont know what to say this time- my exams starts from tmrw so updates may be slow asf BUT i will try to do atleast 1 hour per day"}, {"post_id": 22782, "ts": "2026-07-05T11:19:46Z", "hours": 4.0697, "text": "(v0.0.8(G)-pre-alpha) More Optimization Changelog: (v0.0.8(G)-pre-alpha) Optimization (5) : Omg omg i think i actually did it! what i did was kinda brilliant ngl- So here we go: 1. Whenvever the trees/models are outside the players view they are removed! so like they are not rendered!! Saves cpu and gpu. 2. The snowfall thing is not static now it moves with the player so that only a small area is gettingg the snowfall and a lot less particles is rendered! Lapsed (v0.0.8(F)-pre-alpha) Optimization (4) : I dont even know at this point what i did but i think i increased the frame rates a bit? but i learnt that voxelGI was not performant for my use case so i removed that- also changed the visibility ranges a bit and more i forgo Lapsed (v0.0.8(E)-pre-alpha) Optimization (3) & FPS : A nice session where i sat down and did more optimization- ah yes optimization is the key to life and happiness, I am really shocked that i optimized it so hard that it runs smoothly in my poopc with good graphics! SubViewport and TextureRect for the win, also added fps counter Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations Complete Death animation Notes: HOLY SMOKES! (no pun intended) IM PUSHING 40 FPS!! when i compiled. Normally im getting like 36fps! thats a huge improvement from 9fps that i was getting initally! finally i can do some other stuff lmao"}, {"post_id": 22175, "ts": "2026-07-04T14:38:40Z", "hours": 4.7792, "text": "(v0.0.8(D)-pre-alpha) Particles (and) Optimization Changelog: (v0.0.8(D)-pre-alpha) Optimization (2) : Okay hol’up wait a min- I think i might have actually pulled it! The game is SOOO much optimized now! Not completed but its almost there. So i prebaked the lighting, reduced the scale of the screen cuz i was already doing it in the shader, decreased the shadow radius and more stuff that i may or may not have forgotten in the 3/4 hours that i worked CONTINUOUSLY- Lapsed (v0.0.8(C)-pre-alpha) Death Particles : Okay so for a long time ive been trying to do this but havent (silly me) but here we go- added particle effects for when the silhouettes are “found” they are yellowish and spread out i plan on making some sound effects. Also i did something for the macOS compilation lmao Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations (WIP! close to finishing ig) Notes: Thats so crazy! The game finally runs smoothly on my godforsaken pc. I had to do a LOT of stuff and research, but ngl ChatGPT also came in clutch!"}, {"post_id": 21500, "ts": "2026-07-03T14:21:19Z", "hours": 2.5083, "text": "(v0.0.8(B)-pre-alpha) Models and Optimization(?) Changelog: (v0.0.8(B)-pre-alpha) Particle Optimization (1) : wild stuff- so i wanted to optimize it but guess what i think i did the complete opposite oh well i did reduce the particle count by a ton- we’ll see what happens Lapsed (v0.0.8(A)-pre-alpha) Boulder, Grass, Flowers & Fireflies : Pretty productive session! separated the boulder, flower and grass models. but ngl the boulder gave me a hard ass time idk why 💀 Anyways i cooked up the fireflies scene which i am SUPER proud of (since i didnt use any external help- okay u got me just a bit tho) Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations (WIP) Notes: OKay yo- idk what but i did something 💀"}, {"post_id": 21337, "ts": "2026-07-03T07:02:01Z", "hours": 1.4731, "text": "(v0.0.7(I)-pre-alpha) Spooky Scary Silhouettes Changelog: (v0.0.7(I)-pre-alpha) Silhouette (4) - The Special One : So yesterday I couldnt seem to add the special silhouette in the game but oh well added it in this short session Lapsed (v0.0.7(H)-pre-alpha) Silhouette (3) - Placement : Did a lot of minor stuff (like fixing the material of the log cabin) but the major thing done was the silhouettes’ placement- there are 9 silhouettes, 8 in the wild and one in the cabin, ill make the one in the cabin “special” tmrw! Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks Particle Optimizations Notes: Scawy Scawy Silhouettes! Tiny devlog/journal lolz will work more today"}, {"post_id": 20742, "ts": "2026-07-02T13:07:31Z", "hours": 3.7628, "text": "(v0.0.7(G)-pre-alpha) Scary Silhouettes Changelog: (v0.0.7(G)-pre-alpha) Silhouette (2) - Functionality : OMG OMGO MGOMGOMGOpfjhnguiklhdstg- IDK HOW! but whatever i wanted to has been done! Im really shocked at this! but yeah i gotta thank chatgpt a ton but i got through each line of code and did NOT blindly copy them- I think next time i can do this by my own Lapsed (v0.0.7(F)-pre-alpha) Silhouette (1) - Modeling : I’ve been waiting to make this model for sooo sooo long and finally i made it! Def took some inspiration from the SCP 096 and the models by Seb for his game. Other than the model I also made the scene with a light and fog which seems crazy good ingame Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks Particle Optimizations Notes: Holy lockin! i did like 4 hours today- i need to lock in like this moreeeeeeeeeee AAA- i may get burnt out but yeah only 60 more days to go"}, {"post_id": 20142, "ts": "2026-07-01T18:04:51Z", "hours": 0.9325, "text": "(0.0.7(E)-pre-alpha) Foggy fog Changelog: (v0.0.7(E)-pre-alpha) Volumetric Fog & Polishes : Added Volumetric Fog as per the request of Hridya! Also made numerous tiny polishes to the group texture, snowfall particle effects. I experimented with the snowfall thing with the player but it did NOT look good so i had to revert back to the normal thing :P Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks Particle Optimizations Notes: ahh i couldnt work on this yesterday because i was so swamped- hopefully i can work a lot tmrw!"}, {"post_id": 19345, "ts": "2026-06-30T16:57:13Z", "hours": 2.0328, "text": "(0.0.7(D)-pre-alpha) Snowfalllllllllllllll Changelog: (v0.0.7(D)-pre-alpha) Snowfall (1.2) : Finally managed to fix the issue that was causing the glitched snowfall rendering (again, ty seb) Edited the materials thingy and adjusted the settings in general made it so that it looks good! i really like it Lapsed (v0.0.7(C)-pre-alpha) Snowfall (1.1) : Added the snowfall particles (GPU) but it is kinda bugged right now. I am pushing premature scene because @seb will help me out on this :3 Lets hope we can troubleshoot and fix this together! Lapsed v0.0.7(B)-pre-alpha) Mountains (2) - Texture : I did the UV mapping and the texturing for the mountain model, Had to take a bit of help from my friend @seb for the UV wrapping settings but yeah! did all the stuff by myself! Lapsed Next Step(s): Wooden Torch model Mountains Silhouettes! Sounds Notes: Im so chopped rn- Barely keeping my eyes open… Will devlog properly tmrw"}, {"post_id": 18205, "ts": "2026-06-29T08:31:00Z", "hours": 3.5006, "text": "(0.0.7(A)-pre-alpha) High Mountains Changelog: (v0.0.7(A)-pre-alpha) Mountains (1) - Modelling : really really like the model i have cooked up here, though the texturing is left the base model is looking really cool. The spiky nature kinda symbolizes danger and overall i just think its perfect Lapsed (v0.0.6(I)-pre-alpha) Torches (Placement - 3) : got a lot of work done- the torches are now placed along the path! I’ve done something that i think is really cool. here we go! So i added more torches on the main path to subsconsciously provoke the players to follow the path, on the other hand the tributaries have less! Lapsed Next Step(s): Wooden Torch model Mountains IN PROGRESS!! Silhouettes! Sounds Blizzard (Storms!) Notes: Didnt go to school today cuz i was studying at home lol but got some great work done! ill make the textures next"}, {"post_id": 17570, "ts": "2026-06-28T12:36:02Z", "hours": 2.8669, "text": "(0.0.6(H)-pre-alpha) One stick and a Coal ore Changelog: (0.0.6(H)-pre-alpha) Toggle Shaders : Accidentally paused the shaders in the previous commit so here we go! Re enabling them (0.0.6(G)-pre-alpha) Torches (Placement - 2) : Added several torches in the scene BUT its not completed yet! I spent a good chunk of this time wondering why i was having some lag and low fps but i coudlnt seem to solve em so i left it as a problem for future me Lapsed (0.0.6(F)-pre-alpha) Torches (Model - 1) : I added the torch model! It has a smoke particle effet and a subtle yellowish lighting. The smoke part was truly trivial and i had to use a yt tutorial to make it. BUT the torch model is made all by myself and im pretty satisfied with it! Lapsed Next Step(s): Wooden Torch model Placements left More models and stuff Silhouettes! Sounds Blizzard (Storms!) Notes: I really love the torch model and texture and the lighting- ykw everything in general. I have some optimizations to worry about tho (particles n stuff)"}, {"post_id": 16785, "ts": "2026-06-27T07:54:27Z", "hours": 1.37, "text": "(0.0.6(E)-pre-alpha) Shader, Shader, and Shader! Changelog: (0.0.6(E)-pre-alpha) Shaders (3) : Finalized the shaders setting to make it look peak. hid the extreme blur effect, dither and noise. Made it look more pleasing and not annoying. (0.0.6(D)-pre-alpha) Shaders (2) : New shader now, the previous shader was eh so i found a new one and then modified it a lot! I love each and evey part of it EXCEPT the vignetting i feel like something is wrong. Ill fix it soon tm. also ngl i used chatgpt to understand how these stuff works so yeah :P but dw its not more than 30% ai Next Step(s): Wooden Torch model More models and stuff Silhouettes! Sounds Blizzard (Storms!) Notes: I wasnt happy with the previous shader because there were some irritating artifacts so i switched up the shader to one that i like!"}, {"post_id": 15124, "ts": "2026-06-24T16:05:08Z", "hours": 2.2731, "text": "(0.0.6(C)-pre-alpha) Nighttime & Shaders!! Changelog: (0.0.6(C)-pre-alpha) Shaders (1) : After a lot of trial and errors I finally added the Shader! The environment or the atmosphere ia really taking shape! Im telling u this is gonna be epic!! I am liking the development process a ton though it feels a bit overwhelming sometimes Lapsed (0.0.6(B)-pre-alpha) Nighttime & Fog : This is lowkey peak u cant argue with ts bruh. I know that the lighting and the fog isnt perfect but hey! im slowly learning everything! Today I made the game into a night scene with some fog! looks cool ngl Lapsed (0.0.6(A)-pre-alpha) Upgrade to Godot 4.7 : Nothing much, just upgraded Godot version to 4.7 - the latest one because: 1. I can 2. You cannot stop me 3. Yes Next Step(s): Add trees More models and stuff “3. Shaders Night Time Silhouettes! Sounds Blizzard (Storms!) Notes: I really really love how the game is turning into! THough i know the lore (kinda) and have an idea for the chapter 0 i.e. the prologue I am not sure how to progress it into chapter 1. Well i do have an idea but nly time will tell!"}, {"post_id": 13076, "ts": "2026-06-21T12:04:09Z", "hours": 1.5281, "text": "(v0.0.5(F)-pre-alpha) Forest Design (3) Changelog: (v0.0.5(F)-pre-alpha) Forest Design (Part-3) : Last and final lapse of the forest! The pine/taiga forest has been completed. Nowww- its time for me to add shaders, make it fog, making it night time, etc., This was extremely tiring :/ but was worth it :D Lapsed - Next Step(s): Add trees More models and stuff Shaders Night Time Notes: After so so long time I worked on Vestige again! Looking forward to making more devlogs soon!"}, {"post_id": 9159, "ts": "2026-06-15T10:40:23Z", "hours": 0.8175, "text": "(v0.0.5(E)-pre-alpha) Forest Design (2) Changelog: (v0.0.5(E)-pre-alpha) Forest Design (Part-2) : Did a bit more of the forest, slowly increases the forest cover! Since my school has opened im working a bit less :/. No worries ill slowly keep doing stuff. Added some TODOS Lapsed Next Step(s): Add trees (in progress) More models and stuff Shaders Notes: School started from today so less work :/"}, {"post_id": 8650, "ts": "2026-06-14T11:34:12Z", "hours": 1.6194, "text": "(v0.0.5(D)-pre-alpha) Forest Design && Polishing Changelog: (v0.0.5(D)-pre-alpha) Forest Design (Part-1) : After polishing and giving the finishing touches to the tree models and the chapter 0 map, I am now in the process of level design. Firstly ill make the forest and then go on to the cabin, other models, shaders and gameplay stuff. Its slowly coming together frfrfrfr. Lapsed (v0.0.5(C)-pre-alpha) Polishing (some) Models : hey woud you look at that! im back 🔥anyywayyssss in this lapse i polished the tree models such that the leaves are collision free and then I also changed the texture mode to seem more pixelated. Changed the map a bit to make the trails better. Lapsed Next Step(s): Add trees (in progress) More models and stuff Shaders Notes: Im coming back after soo soo long because of two main reasons: 1) I was skeptical of Lapse(it was bugging) ANDD 2) i was very bust these days. Welp my school starts tmrw"}, {"post_id": 6122, "ts": "2026-06-10T09:17:02Z", "hours": 3.6831, "text": "(v0.0.5(B)-pre-alpha) Log Cabin Changelog: (v0.0.5(B)-pre-alpha) Log Cabin : Very very happy with the log cabin model. But it took soooo sooo mucj time omg. I am too tired to write more. Sorry. Vestige: Log Cabin : holy moly, this is crazy! I LITERALLY SPENT 3 HOURS of my life making a log cabin… Wow. Hey it was kinda worth it! I imported this into godot but uhh its doing quirky things lets see if i can fix! Next Step(s): Add trees Shaders Log Cabin Model Notes: The UV on the log cabin was hell. omfg"}, {"post_id": 5410, "ts": "2026-06-09T05:07:59Z", "hours": 2.4636, "text": "(v0.0.5(A)-pre-alpha) PLaying the Map! Changelog: (v0.0.5(A)-pre-alpha) Map + Player : OKay we are stepping up to the big guns now! In this commit I have added a Map for chapter 0 AND also added the player model! I KNOW that the grass and trail textures are not the great and they look “not good” but I think ill polish it when the core game mechanics is done!z Vestige: Map & Player : Wow this is SO COOLL! I am really enjoying blender AND godot. So here I made the map for chapter 0 (prologue) and a player character! Definitely cooking Next Step(s): Add trees Shaders Log Cabin Model Notes: Okay! I am starting to learn blender! I am really enjoyying the process!"}, {"post_id": 4175, "ts": "2026-06-07T12:14:44Z", "hours": 2.5375, "text": "(v0.0.4-pre-alpha) Model & design Changelog: (v0.0.4-pre-alpha) Level Design + Bench Model : Designed the chapter 0– like a mind map, now its time for me to start 3d modelling and making the game. And a bench model for chapter 0 Vestige: Chapter 0- Level Design : Just designed the chapter 0– like a mind map, now its time for me to start 3d modelling and making the game Vestige: Bench Model : This is a bench model for chapter 0. I spent so much time making this. i think i gained experience and the next models will be faster Next Step(s): Start Modeling Make ground Make a player object and controller. Notes: OH MY GOD! I MODELED THAT? I really really dont believe it! It looks soooo good."}, {"post_id": 3428, "ts": "2026-06-06T11:18:54Z", "hours": 1.6908, "text": "(v0.0.3(A)-pre-alpha) Think, Write, Repeat. Changelog: (v0.0.3(B)-pre-alpha) Game Design: Chapter 0 : Oh my gawd! The prologue sequence is so so so cool! I just love it. Ive laid out a complete map of chapter 0! NOW i can finally start working on the first chaapter without procrastinating more lol. (v0.0.3(A)-pre-alpha) Story: Chapter 0 & 1 : Yoooo finally wrote the complete chapter 0 (the prologue) and polished chapter1 even more. Its really coming together. Now I will move on to the game design. Like each action and tiny details. After I’m done with Chapter 0 gamedesign ill start making it Next Step(s): Complete Storyline for Ch0 and Ch1 Create a Script for Chapter 0 Start modeling Notes: THis is SOOO time and energy consuming! Storyboarding is NOT easy lmao. Anyways i finished the story for Ch0 and Ch1 and script for Ch0. Now ill start modeling. frfr"}, {"post_id": 2834, "ts": "2026-06-05T17:46:32Z", "hours": 1.7725, "text": "(v0.0.3-pre-alpha) Modeling and Writing! Changelog: (v0.0.3-pre-alpha) Test Model: Nightstand : OKAY this my first time using blender and I used a tutorial to guide me through. THough it looks, well, “okay” im not satisfied. Ill make a better one. Just trust me on this (v0.0.2(B)-pre-alpha) Chapter 1: Story : Okay! I’ve polished and completed the chapter1 story line. Ive tweaked the backstory a bit to stay realistic. I really really like the direction im going for while keeping the scope uhh managable. Ive so many places where I can improve upon! ive kept it open ended because i have chapter 2 planned 🔥 Next Step(s): Complete Storyline for Ch1 Create a Script for Chapter 1 Start modeling Notes: OKayy i really liked modeling, i think im getting a hang of it! AALSOOOO im super proud of the story line im cooking rn"}, {"post_id": 898, "ts": "2026-06-02T16:57:43Z", "hours": 1.0978, "text": "(v0.0.2(A)-pre-alpha) Setup and Narrative! Changelog: (v0.0.2(A)-pre-alpha) Narrative (Lore) and Versioning : Okay so added a one line plot and brain stormed and wrote a mini story which is the internal plot which should NOT be read as the player. This only serves to streamline the development. Anyways the plot may be subject to change. (v0.0.1-pre-alpha) Godot Setup! : Nothing crazy just setup Godot (first time btw!) (v0.0.0-pre-alpha) Initial Commit : Initial commit- what else do u expect me to say?? (/s i love yall) Next Step(s): Complete Storyline Create a Script for Chapter 1 Start modelling Notes: To be absolutely honest with yall this is my first time making a game (by myself) and Im super excited! Cant wait to start modelling and making the game!"}]...
|
Title: Vestige by @Rupnil | Stardance
Devlogs:
(v Title: Vestige by @Rupnil | Stardance
Devlogs:
(v0.0.2(A)-pre-alpha) Setup and Narrative! Changelog: (v0.0.2(A)-pre-alpha) Narrative (Lore) and Versioning : Okay so added a one line plot and brain stormed and wrote a mini story which is the internal plot which should NOT be read as the player. This only serves to streamline the development. Anyways the plot may be subject to change. (v0.0.1-pre-alpha) Godot Setup! : Nothing crazy just setup Godot (first time btw!) (v0.0.0-pre-alpha) Initial Commit : Initial commit- what else do u expect me to say?? (/s i love yall) Next Step(s): Complete Storyline Create a Script for Chapter 1 Start modelling Notes: To be absolutely honest with yall this is my first time making a game (by myself) and Im super excited! Cant wait to start modelling and making the game!
(v0.0.3-pre-alpha) Modeling and Writing! Changelog: (v0.0.3-pre-alpha) Test Model: Nightstand : OKAY this my first time using blender and I used a tutorial to guide me through. THough it looks, well, “okay” im not satisfied. Ill make a better one. Just trust me on this (v0.0.2(B)-pre-alpha) Chapter 1: Story : Okay! I’ve polished and completed the chapter1 story line. Ive tweaked the backstory a bit to stay realistic. I really really like the direction im going for while keeping the scope uhh managable. Ive so many places where I can improve upon! ive kept it open ended because i have chapter 2 planned 🔥 Next Step(s): Complete Storyline for Ch1 Create a Script for Chapter 1 Start modeling Notes: OKayy i really liked modeling, i think im getting a hang of it! AALSOOOO im super proud of the story line im cooking rn
(v0.0.3(A)-pre-alpha) Think, Write, Repeat. Changelog: (v0.0.3(B)-pre-alpha) Game Design: Chapter 0 : Oh my gawd! The prologue sequence is so so so cool! I just love it. Ive laid out a complete map of chapter 0! NOW i can finally start working on the first chaapter without procrastinating more lol. (v0.0.3(A)-pre-alpha) Story: Chapter 0 & 1 : Yoooo finally wrote the complete chapter 0 (the prologue) and polished chapter1 even more. Its really coming together. Now I will move on to the game design. Like each action and tiny details. After I’m done with Chapter 0 gamedesign ill start making it Next Step(s): Complete Storyline for Ch0 and Ch1 Create a Script for Chapter 0 Start modeling Notes: THis is SOOO time and energy consuming! Storyboarding is NOT easy lmao. Anyways i finished the story for Ch0 and Ch1 and script for Ch0. Now ill start modeling. frfr
(v0.0.4-pre-alpha) Model & design Changelog: (v0.0.4-pre-alpha) Level Design + Bench Model : Designed the chapter 0– like a mind map, now its time for me to start 3d modelling and making the game. And a bench model for chapter 0 Vestige: Chapter 0- Level Design : Just designed the chapter 0– like a mind map, now its time for me to start 3d modelling and making the game Vestige: Bench Model : This is a bench model for chapter 0. I spent so much time making this. i think i gained experience and the next models will be faster Next Step(s): Start Modeling Make ground Make a player object and controller. Notes: OH MY GOD! I MODELED THAT? I really really dont believe it! It looks soooo good.
(v0.0.5(A)-pre-alpha) PLaying the Map! Changelog: (v0.0.5(A)-pre-alpha) Map + Player : OKay we are stepping up to the big guns now! In this commit I have added a Map for chapter 0 AND also added the player model! I KNOW that the grass and trail textures are not the great and they look “not good” but I think ill polish it when the core game mechanics is done!z Vestige: Map & Player : Wow this is SO COOLL! I am really enjoying blender AND godot. So here I made the map for chapter 0 (prologue) and a player character! Definitely cooking Next Step(s): Add trees Shaders Log Cabin Model Notes: Okay! I am starting to learn blender! I am really enjoyying the process!
(v0.0.5(B)-pre-alpha) Log Cabin Changelog: (v0.0.5(B)-pre-alpha) Log Cabin : Very very happy with the log cabin model. But it took soooo sooo mucj time omg. I am too tired to write more. Sorry. Vestige: Log Cabin : holy moly, this is crazy! I LITERALLY SPENT 3 HOURS of my life making a log cabin… Wow. Hey it was kinda worth it! I imported this into godot but uhh its doing quirky things lets see if i can fix! Next Step(s): Add trees Shaders Log Cabin Model Notes: The UV on the log cabin was hell. omfg
(v0.0.5(D)-pre-alpha) Forest Design && Polishing Changelog: (v0.0.5(D)-pre-alpha) Forest Design (Part-1) : After polishing and giving the finishing touches to the tree models and the chapter 0 map, I am now in the process of level design. Firstly ill make the forest and then go on to the cabin, other models, shaders and gameplay stuff. Its slowly coming together frfrfrfr. Lapsed (v0.0.5(C)-pre-alpha) Polishing (some) Models : hey woud you look at that! im back 🔥anyywayyssss in this lapse i polished the tree models such that the leaves are collision free and then I also changed the texture mode to seem more pixelated. Changed the map a bit to make the trails better. Lapsed Next Step(s): Add trees (in progress) More models and stuff Shaders Notes: Im coming back after soo soo long because of two main reasons: 1) I was skeptical of Lapse(it was bugging) ANDD 2) i was very bust these days. Welp my school starts tmrw
(v0.0.5(E)-pre-alpha) Forest Design (2) Changelog: (v0.0.5(E)-pre-alpha) Forest Design (Part-2) : Did a bit more of the forest, slowly increases the forest cover! Since my school has opened im working a bit less :/. No worries ill slowly keep doing stuff. Added some TODOS Lapsed Next Step(s): Add trees (in progress) More models and stuff Shaders Notes: School started from today so less work :/
(v0.0.5(F)-pre-alpha) Forest Design (3) Changelog: (v0.0.5(F)-pre-alpha) Forest Design (Part-3) : Last and final lapse of the forest! The pine/taiga forest has been completed. Nowww- its time for me to add shaders, make it fog, making it night time, etc., This was extremely tiring :/ but was worth it :D Lapsed - Next Step(s): Add trees More models and stuff Shaders Night Time Notes: After so so long time I worked on Vestige again! Looking forward to making more devlogs soon!
(0.0.6(C)-pre-alpha) Nighttime & Shaders!! Changelog: (0.0.6(C)-pre-alpha) Shaders (1) : After a lot of trial and errors I finally added the Shader! The environment or the atmosphere ia really taking shape! Im telling u this is gonna be epic!! I am liking the development process a ton though it feels a bit overwhelming sometimes Lapsed (0.0.6(B)-pre-alpha) Nighttime & Fog : This is lowkey peak u cant argue with ts bruh. I know that the lighting and the fog isnt perfect but hey! im slowly learning everything! Today I made the game into a night scene with some fog! looks cool ngl Lapsed (0.0.6(A)-pre-alpha) Upgrade to Godot 4.7 : Nothing much, just upgraded Godot version to 4.7 - the latest one because: 1. I can 2. You cannot stop me 3. Yes Next Step(s): Add trees More models and stuff “3. Shaders Night Time Silhouettes! Sounds Blizzard (Storms!) Notes: I really really love how the game is turning into! THough i know the lore (kinda) and have an idea for the chapter 0 i.e. the prologue I am not sure how to progress it into chapter 1. Well i do have an idea but nly time will tell!
(0.0.6(E)-pre-alpha) Shader, Shader, and Shader! Changelog: (0.0.6(E)-pre-alpha) Shaders (3) : Finalized the shaders setting to make it look peak. hid the extreme blur effect, dither and noise. Made it look more pleasing and not annoying. (0.0.6(D)-pre-alpha) Shaders (2) : New shader now, the previous shader was eh so i found a new one and then modified it a lot! I love each and evey part of it EXCEPT the vignetting i feel like something is wrong. Ill fix it soon tm. also ngl i used chatgpt to understand how these stuff works so yeah :P but dw its not more than 30% ai Next Step(s): Wooden Torch model More models and stuff Silhouettes! Sounds Blizzard (Storms!) Notes: I wasnt happy with the previous shader because there were some irritating artifacts so i switched up the shader to one that i like!
(0.0.6(H)-pre-alpha) One stick and a Coal ore Changelog: (0.0.6(H)-pre-alpha) Toggle Shaders : Accidentally paused the shaders in the previous commit so here we go! Re enabling them (0.0.6(G)-pre-alpha) Torches (Placement - 2) : Added several torches in the scene BUT its not completed yet! I spent a good chunk of this time wondering why i was having some lag and low fps but i coudlnt seem to solve em so i left it as a problem for future me Lapsed (0.0.6(F)-pre-alpha) Torches (Model - 1) : I added the torch model! It has a smoke particle effet and a subtle yellowish lighting. The smoke part was truly trivial and i had to use a yt tutorial to make it. BUT the torch model is made all by myself and im pretty satisfied with it! Lapsed Next Step(s): Wooden Torch model Placements left More models and stuff Silhouettes! Sounds Blizzard (Storms!) Notes: I really love the torch model and texture and the lighting- ykw everything in general. I have some optimizations to worry about tho (particles n stuff)
(0.0.7(A)-pre-alpha) High Mountains Changelog: (v0.0.7(A)-pre-alpha) Mountains (1) - Modelling : really really like the model i have cooked up here, though the texturing is left the base model is looking really cool. The spiky nature kinda symbolizes danger and overall i just think its perfect Lapsed (v0.0.6(I)-pre-alpha) Torches (Placement - 3) : got a lot of work done- the torches are now placed along the path! I’ve done something that i think is really cool. here we go! So i added more torches on the main path to subsconsciously provoke the players to follow the path, on the other hand the tributaries have less! Lapsed Next Step(s): Wooden Torch model Mountains IN PROGRESS!! Silhouettes! Sounds Blizzard (Storms!) Notes: Didnt go to school today cuz i was studying at home lol but got some great work done! ill make the textures next
(0.0.7(D)-pre-alpha) Snowfalllllllllllllll Changelog: (v0.0.7(D)-pre-alpha) Snowfall (1.2) : Finally managed to fix the issue that was causing the glitched snowfall rendering (again, ty seb) Edited the materials thingy and adjusted the settings in general made it so that it looks good! i really like it Lapsed (v0.0.7(C)-pre-alpha) Snowfall (1.1) : Added the snowfall particles (GPU) but it is kinda bugged right now. I am pushing premature scene because @seb will help me out on this :3 Lets hope we can troubleshoot and fix this together! Lapsed v0.0.7(B)-pre-alpha) Mountains (2) - Texture : I did the UV mapping and the texturing for the mountain model, Had to take a bit of help from my friend @seb for the UV wrapping settings but yeah! did all the stuff by myself! Lapsed Next Step(s): Wooden Torch model Mountains Silhouettes! Sounds Notes: Im so chopped rn- Barely keeping my eyes open… Will devlog properly tmrw
(0.0.7(E)-pre-alpha) Foggy fog Changelog: (v0.0.7(E)-pre-alpha) Volumetric Fog & Polishes : Added Volumetric Fog as per the request of Hridya! Also made numerous tiny polishes to the group texture, snowfall particle effects. I experimented with the snowfall thing with the player but it did NOT look good so i had to revert back to the normal thing :P Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks Particle Optimizations Notes: ahh i couldnt work on this yesterday because i was so swamped- hopefully i can work a lot tmrw!
(v0.0.7(G)-pre-alpha) Scary Silhouettes Changelog: (v0.0.7(G)-pre-alpha) Silhouette (2) - Functionality : OMG OMGO MGOMGOMGOpfjhnguiklhdstg- IDK HOW! but whatever i wanted to has been done! Im really shocked at this! but yeah i gotta thank chatgpt a ton but i got through each line of code and did NOT blindly copy them- I think next time i can do this by my own Lapsed (v0.0.7(F)-pre-alpha) Silhouette (1) - Modeling : I’ve been waiting to make this model for sooo sooo long and finally i made it! Def took some inspiration from the SCP 096 and the models by Seb for his game. Other than the model I also made the scene with a light and fog which seems crazy good ingame Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks Particle Optimizations Notes: Holy lockin! i did like 4 hours today- i need to lock in like this moreeeeeeeeeee AAA- i may get burnt out but yeah only 60 more days to go
(v0.0.7(I)-pre-alpha) Spooky Scary Silhouettes Changelog: (v0.0.7(I)-pre-alpha) Silhouette (4) - The Special One : So yesterday I couldnt seem to add the special silhouette in the game but oh well added it in this short session Lapsed (v0.0.7(H)-pre-alpha) Silhouette (3) - Placement : Did a lot of minor stuff (like fixing the material of the log cabin) but the major thing done was the silhouettes’ placement- there are 9 silhouettes, 8 in the wild and one in the cabin, ill make the one in the cabin “special” tmrw! Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks Particle Optimizations Notes: Scawy Scawy Silhouettes! Tiny devlog/journal lolz will work more today
(v0.0.8(B)-pre-alpha) Models and Optimization(?) Changelog: (v0.0.8(B)-pre-alpha) Particle Optimization (1) : wild stuff- so i wanted to optimize it but guess what i think i did the complete opposite oh well i did reduce the particle count by a ton- we’ll see what happens Lapsed (v0.0.8(A)-pre-alpha) Boulder, Grass, Flowers & Fireflies : Pretty productive session! separated the boulder, flower and grass models. but ngl the boulder gave me a hard ass time idk why 💀 Anyways i cooked up the fireflies scene which i am SUPER proud of (since i didnt use any external help- okay u got me just a bit tho) Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations (WIP) Notes: OKay yo- idk what but i did something 💀
(v0.0.8(D)-pre-alpha) Particles (and) Optimization Changelog: (v0.0.8(D)-pre-alpha) Optimization (2) : Okay hol’up wait a min- I think i might have actually pulled it! The game is SOOO much optimized now! Not completed but its almost there. So i prebaked the lighting, reduced the scale of the screen cuz i was already doing it in the shader, decreased the shadow radius and more stuff that i may or may not have forgotten in the 3/4 hours that i worked CONTINUOUSLY- Lapsed (v0.0.8(C)-pre-alpha) Death Particles : Okay so for a long time ive been trying to do this but havent (silly me) but here we go- added particle effects for when the silhouettes are “found” they are yellowish and spread out i plan on making some sound effects. Also i did something for the macOS compilation lmao Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations (WIP! close to finishing ig) Notes: Thats so crazy! The game finally runs smoothly on my godforsaken pc. I had to do a LOT of stuff and research, but ngl ChatGPT also came in clutch!
(v0.0.8(G)-pre-alpha) More Optimization Changelog: (v0.0.8(G)-pre-alpha) Optimization (5) : Omg omg i think i actually did it! what i did was kinda brilliant ngl- So here we go: 1. Whenvever the trees/models are outside the players view they are removed! so like they are not rendered!! Saves cpu and gpu. 2. The snowfall thing is not static now it moves with the player so that only a small area is gettingg the snowfall and a lot less particles is rendered! Lapsed (v0.0.8(F)-pre-alpha) Optimization (4) : I dont even know at this point what i did but i think i increased the frame rates a bit? but i learnt that voxelGI was not performant for my use case so i removed that- also changed the visibility ranges a bit and more i forgo Lapsed (v0.0.8(E)-pre-alpha) Optimization (3) & FPS : A nice session where i sat down and did more optimization- ah yes optimization is the key to life and happiness, I am really shocked that i optimized it so hard that it runs smoothly in my poopc with good graphics! SubViewport and TextureRect for the win, also added fps counter Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations Complete Death animation Notes: HOLY SMOKES! (no pun intended) IM PUSHING 40 FPS!! when i compiled. Normally im getting like 36fps! thats a huge improvement from 9fps that i was getting initally! finally i can do some other stuff lmao
(v0.0.9(B)-alpha) Animations! Changelog: (v0.0.9(B)-alpha) Waking up & Counter Animation : So added the waking up animation - this was a new thing for me that i think i leanrt! the animation player is a really cool feature! Also now the silhouette counter shows up only after the first silhouette is found Lapsed (v0.0.9(A)-alpha) Death Animation : Fixed a bug (ig? idk if u can call it that) that made it so whenever u looked away from the silhouette the animation would completely freeze. Turns out there was a VisibleOnScreenEnabler3D child which froze the process- Its fixed now! Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations Complete Death animation Walking animation Notes: uhh i dont know what to say this time- my exams starts from tmrw so updates may be slow asf BUT i will try to do atleast 1 hour per day
(v0.0.9(E)-alpha) More erm Animations! Changelog: (v0.0.9(E)-alpha) Silhouette Spawning, Flickering Animations (2) : Yay i finally made the silhouette spawning animation! it looks sick! the way it slowly comes into view- really cool i also did a bunch of things here and there that im not going into detail :3 Lapsed (v0.0.9(D)-alpha) Silhouette Spawning (1) : Erm uhh idk- i experimented with both script and animationplayer animations but i couldnt seem to get them to work - the script seemed to fail completely while the animation player got me some success but not enough- I will look in to this tmrw! Lapsed Next Step(s): Models of the silhouettes Boulders / Rocks (WIP) Particle Optimizations Complete Death animation Walking animation Notes: yoo gng i had my regional language and physics exam today and i lwk think i did pretty well! Anyways i did some work and i think ill do more now!
(v0.0.10(A)-alpha) Walk and Map Redesign Changelog: (v0.0.10(A)-alpha) Map Redesigning (1) : I think im going to go on a different direction with the prologue scene! i changed the pathways because i think it was too complex and looping- I am also thinking of turning the log cabin into an watch tower for more ominous feel! im working on the story hopefully i can figure it out! Lapsed (v0.0.9(F)-alpha) Walk Animation (Camera Shake) : Yoooo! holy smokes this look epic spent a good chunk figuring out how to make the walk animation turns out its easier to do it through code- thats why i ended the lapse prematurely and worked on it in Rider IDE! it look so cool rn it has head bobbing in all three dimensions! Lapsed Next Step(s): Boulders / Rocks (WIP) Walking animation Story Redesign Notes: I had some different ideas about the story and im working on it- redesigned the map a bit! i think ill do more stuff
(v0.0.10(C)-alpha) Story and Map Redesign Changelog: (v0.0.10(C)-alpha) Story + Map Redesigning (2) : So as i am changing the narrative i need to update the map accordingly, A very small update because my mouse stopped wrking but oh well i just changed some textures and added trees. Also making a story narrative in 2nd person ig? Lapsed (v0.0.10(B)-alpha) Story Redesign (1) : Brainstormed a lot of stuff- and rewrote the prologue storyline- the old one was pretty basic i like the new one more and I mostly changed it because i am thinking of the next chapter too and i want a seamless transition- just hang on with me ill cook something truly epic :3 Next Step(s): Boulders / Rocks (WIP) Walking animation Story Redesign (WIP) Notes: OKay today ill lock tf in my exams just finished!
(v0.1.1(A)-alpha) Map Redesign and Improved mechanics! Changelog: (v0.1.1(A)-alpha) Improved Silhouette Mechanics : oh my! it looks so sick! il definitely play test this once more but heres what i did in brief: Previously there used to be different silhouttes but NOW theres only one that “teleports” when u reach it as if guiding you somewhere – to the watchtower :3 Lapsed (v0.0.10(E)-alpha) Map Redesigning (4) : I think i am done with the forest/map layout for now im pretty happy! plugged off some paths that i didnt want the player to go through- removed a ton of legacy code. Next up i think ill work on the silhouette mechanics which i cooked up in the new narrative! wish me luck! Lapsed (v0.0.10(D)-alpha) Story + Map Redesigning (3) : Again one of the shortest lapses i have made but its enough for me to get the 1 hour streak lmao- I promise Ill work honestly from tmrw, Exams finished just today and im exhausted asf- Please excuse me! Made updates to the story too Next Step(s): Watch Tower Model Walking animation Story Redesign Improved Silhouette Mechanics Notes: Holy moly! the silhouette (YES SINGULAR) works wonderfully now! its like its guiding you! im really happy with the result i think ill model the watch tower.
(v0.1.1(E)-alpha) Watch Tower (40%)! Changelog: (v0.1.1(E)-alpha) Watch Tower Modelling (4) : erm so nothing productive just me fixing the HUMONGOUS number of vertices that have been created for god knows what. Lmfao i messed up at the end and i have to go back to the last backup Oh well i have tuitions now will do more stuff later Lapsed (v0.1.1(D)-alpha) Watch Tower Modelling (3) : Ah yes, guess why there is no lapse for this update- thats right! Lookout messed up and I cannot recover the lost footage and publish it in lapse. I lwk forgot what i did in this update 💀 You are gonna guess it (v0.1.1(B&C)-alpha) Watch Tower Modelling (1&2) : Guess who forgot to commit THATS RIGHT ITS ME- In this lapse I made the skeleton of the watch tower that is going to be the main attraction of the prologue. I do not have much time and hence i am ending this session prematurely! See you tmrw! More modelling of the watch tower, made the top thing the uhh room? ig tested the model size with the player size in godot and i found that it was a bit small so i fixed that! Made some windows for the watchtower so that everything is visible Lapsed Next Step(s): Watch Tower Model (WIP) Walking animation Story Redesign Improved Silhouette Mechanics — Notes: Ah yes I lost like 30 minutes of lapse yesterdat cuz lookout servers died oh welp! here i am again!
(v0.1.1(I)-alpha) Watch Tower Modelling (60%)! Changelog: (v0.1.1(I)-alpha) Watch Tower Modelling (7) : Nothing much just edited the fences a bit and resized them in order to prevent overlapping. Oh also I made the roof – needs polishing ik but its a start lmao- i want to complete the modelling as soon as possible lwk- Lapsed (v0.1.1(H)-alpha) Watch Tower Modelling (6) : Yoo moving from vertices selection to something different felt awesome! anyways i made the fence? i think thats what its called- On all four sides. I am following a model for inspiration and im pretty happy on how it is turning out! Lapsed (v0.1.1(G)-alpha) Watch Tower Modelling (5.3) : FINALLY FINALLY! I have completed optimizing the walls of the watch tower. What i mean is I reduced the number of vertices and faces in the model. This will help me immensely while texturing the UV. I created another backup just in case lmao Lapsed (v0.1.1(F)-alpha) Watch Tower Modelling (5.1 + 5.2) : I tried cleaning up the Watch Tower model but idk how it all got messed up anyways I will try again ONE LAST TIME tommorow if it works it works otherwise i have to roll back to the backup and just forget about cleaning up- i messed up yeah Lapsed Next Step(s): Watch Tower Model (WIP) Watch Tower Texture Notes: i NEED to finish modelling this ASAP gng 💀 wish me luck ill be lockin in today 🔥
(v0.1.1(L)-alpha) Watch Tower Modelling (75%)! Changelog: (v0.1.1(L)-alpha) Watch Tower Modelling (9.3) : Joined a lot of meshes together for efficiency and ease of use. Oh also made the raining v1 for the first layer of the stairs. I have my prize distribution and investiture ceremony so yeah! see ya laterrrrr Lapsed (v0.1.1(K)-alpha) Watch Tower Modelling (9.1 + 9.2) : 1. omg omg omg! stairs in progressssss! Made two flights of stairs- what i mean is i made one flight of stairs and then copied it 💀 Anyways I’m almost done with the modelling i think- The stairs and the windows are the only things are left (oh maybe more optimization :p) 2. Added much More stairs and the topmost layer. I will make the integration of the stairs to the floor tommorow, ALSO i will add the railings and all tmrw I am sleepy as frick! Lapsed (v0.1.1(J)-alpha) Watch Tower Modelling (8) : Wow! what a productive session! Ok so a lot of thing was done. Here’s what i did in short: The legs of the watch tower was made before but in this session i added the supports and support beams! i again followed the model as an inspiration and made the supports! Looks prety cool! Lapsed — Next Step(s): Watch Tower Model (WIP) Watch Tower Texture Notes: um holy shit i am doing a 4? or 5 hour devlog 💀 this is unreal! Anyways this is draggin me sooo muchh but i think im super close to finishing this
(v0.1.1(O)-alpha) Watch Tower Modelling (90%)! Changelog: (v0.1.1(O)-alpha) Watch Tower Modelling (10.3) : hell yeah! completed the guard rails– now uhh some more fences are remaining i promise it wil be done very soon im trying my bestttt Lapsed (v0.1.1(N)-alpha) Watch Tower Modelling (10.2) : Made another layer of guard rails beside the stairs - yeah just that 💀 i have no more words to write here Lapsed (v0.1.1(M)-alpha) Watch Tower Modelling (10.1) : I might work again later after coming back from art classes- but im not sure as i have some tuition work to do :( I also have some pending ssignments! yikes!!! anyways here’s what i did: Made the rails of the stairs with some bars - first and second stairs. did some desmos stuff- Lapsed Next Step(s): Watch Tower Model (WIP) Watch Tower Texture Notes: ok i dont really know what to write hereeee nowwww- maybe the next time
(v0.1.2(A)-alpha) Watch Tower Texturing (2%) Changelog: (v0.1.2(A)-alpha) Watch Tower Texturing (1.1) : Yay so i moved on to the texturing part of the watch toweeeeerr. Just made the door for now but I will accelerate the work from tmrw trust Lapsed (v0.1.1(Q)-alpha) Watch Tower Modelling (11) : oh my gosh! im done with the modelling! well i mean i may add the window margins stuff later but yeah! i am now starting the texturing! i made the glass and experimented with some stuff Lapsed (v0.1.1(P)-alpha) Watch Tower Modelling (10.4) : ok i am being for REAL right now. and u HAVe to believe me when i say that i am FInALLY COMPLETE with the freaking fencing stuff i am NOT doing that again 💀 ok now the only thing left is the window panes and THEn i can finally move on to the texture- Lapsed Next Step(s): Watch Tower Model Watch Tower Texture (WIP) Notes: Yooooo so I finally completed the modelling of the watch tower and started texturing it recently. I have some work to do today so idk how long i can code- Ill try my best tho!
(v0.1.2(C)-alpha) Watch Tower Texturing (45%) Changelog: (v0.1.2(A)-alpha) Watch Tower Texturing (1.5) : Yay so i made the Floor section completely- the floor and the uhh supprts ig lol. THis was fun Ill work more tmrw Lapsed (v0.1.2(A)-alpha) Watch Tower Texturing (1.2 + 1.3 + 1.4) : 1. So nothing much to brag about here- I completed texturing the roof. It was pretty easy ngl - anyways I’ll do the UV unwrapping of the walls next ig. 2. So I unwrapped the UV layout of the walls and its Horrible! It will take so much time to manually fix everything lmao. I did like 50% of the unwrapping today NOT THE TEXTURNG BTW! so we will see tmrw! gn. 3. Completed the UV unwrapping of the walls and also the texturing of the walls! the room section of the texturing is done! moving on to the floor and stairs! Lapsed Next Step(s): Watch Tower Model Watch Tower Texture (WIP) Notes: Aww man i couldnt really focus on this project for the last few days i had so much work! had 2 assignments- submitted them today so i think i can focus in more today!!
(v0.1.2(H)-alpha) Watch Tower Texturing (90%) Changelog: (v0.1.2(H)-alpha) Watch Tower Texturing (4.1) : Okay so nothing but I joined a hell lot of meshes and objects- i mean the stairs portion. And then unwrapped the UV this took me some time but i finished it! next imma fix the UV and then COMPLETE this FOR ONCE AND ALL! Lapsed (v0.1.2(G)-alpha) Watch Tower Texturing (3.4) : Ayy so i completed the Leg section of the watch tower! i agree most of the work is uv unwrapping because of the faces and all yk. But yeah! im making progress only the stairs section left ig!!!! Lapsed (v0.1.2(F)-alpha) Watch Tower Texturing (3.3) : Teeny tiny lapse! just combined and unwrapped the support beams- ill prolly texture it tommorw and then move on to the stairs section Lapsed (v0.1.2(E)-alpha) Watch Tower Texturing (3.1+3.2) : Holy smokes! What a productive day! got so so so much done today – So I completed the Legs, and support beams in this lapse so only the cross supports are left! Also blender crashed like 300 times today– i really need the macbook gng i got the signs already! Lapsed (v0.1.2(D)-alpha) Watch Tower Texturing (2) : Oh another milestone i suppose! I completed the UV unwrapping of the fence and also textured it! I dont have any more stuff to talk about gng im so tired and busy nowadays Lapsed —## Next Step(s): Watch Tower Model Watch Tower Texture (WIP GNG ALMOST) Notes:OK LISTEN! LISTEN I swore to @Keyboard that I wont devlog until I have completed the whole texturing BUT its getting too ridiculous not to- im alr at 7 hours and yeah. Also big ass devlog-
(v0.1.4(B)-alpha) Walking on the Watch Tower (Failed) Changelog: (v0.1.4(B)-alpha) Walking on Stairs (2) : Lwk I just messed around for the whole time I literally do not know wht to do at this point I will revisit this tommorow (I dmed my fwend Seb lmao). I tried many methods but i couldnt seem to be able to make this work tho Lapsed (v0.1.4(A)-alpha) Walking on Stairs (1) : Yo gang I tried my to do the stairs walking thing but as you would expect I completely failed lmfAaooo. Uh I really dont know how to move next but ill try my best Lapsed (v0.1.3-alpha) Mountains Texture Fix : So erm as i completed the watch tower im moving on to other thigns– first i saw that this was in my todo for a while so fixed it lol Lapsed (v0.1.2(K)-alpha) Watch Tower Texturing (4.4) : Shalalala sha boinky! I actually finished the (skeleton) of the watch tower model! Omg i dont believe it! I am feeling a large weight off my shoulders lmfao! But hey its not done yet! I have so so so sos os so much more to do- next up ill fix the stair walking and oh a story redesign! Lapsed (v0.1.2(J)-alpha) Watch Tower Texturing (4.3) : Finally completed texturing the stairs – the railings and the beams are still remaining but ill try to do them soon enough 👍 Lapsed (v0.1.2(I)-alpha) Watch Tower Texturing (4.2) : oh my GOD BRO this is so painful i really hope i could do something else to speed this up but yeah idk- So nothing other than UV unwrap stuff NOT EVEN ONE mesh and i have 2 more to go- I am dying :D Lapsed Next Step(s): Watch Tower Model Watch Tower Texture Auto Jump on Stairs Interior Design of the Watch Tower Notes: Gang I tried my best I literally cannot seem to be able to be able to find a way to make the auto jump thing I need to call the big guns now! ( @seb ) Anyways, I obv completed the watch tower texturing
(v0.1.4(G)-alpha) Moving & Jumping on the Stairs (Passed!) Changelog: (v0.1.4(G)-alpha) Player Movement Rework: Auto-jump (5) : Ok i kinda cheesed it ngl- I was literally being ragebaited because it was NOT working any how. I am using a not so beautiful method but hey it works and thats what counts – ill polish it later dw. Lapsed (v0.1.4(F)-alpha) Player Movement Rework: ?? (4) : wtf? what happened? what did i do? this is insane i really dont know………. soo erm climbing stairs doesnt work and there are some pretty bad bugs like being able to fly- lwk i might revert this session Lapsed (v0.1.4(E)-alpha) Player Movement Rework: Noclip (3) : added a pretty peak thing. Ok so added a noclip feature – this is like the spectator mode in minecraft lmfao. Ive been meant to do this for a long time but here i am! finally added this – this will help me a ton i js know it lol Lapsed (v0.1.4(D)-alpha) Player Movement Rework (2) : Yoo so added a bunch of new things such as air friction, acceleration deceleration and more. I am having one tiny bug tho – that is i cannot turn when jumping but dw ill figure out a way to fix it! Also if u forgot Im doing the player rework just to make the autojump thing Lapsed (v0.1.4(C)-alpha) Player Movement Rework (1) : Okay So i realised it wwill be extremely difficult for me to figure it out on my own, and even the big ones ( @seb ) had no freaking idea lmao. SO what i am doing bnow is following a player/fps controller tutorial where they add this autojump feature :3 Lapsed Next Step(s): Watch Tower Model Watch Tower Texture Auto Jump on Stairs (Done but polishing left) Interior Design of the Watch Tower Core Gameplay Notes: These sessions were lwk ragebait – i will NOT talk about the 4th session cuz i have no freaking idea about it. I found a weird way and workaround to do the auto jump. Although it might not be the most beautiful but it works and thats what counts
(v0.1.5-alpha) Better Movement, Stealth & Environment! Changelog: (v0.1.5-alpha) Environment Rework & Jitter Fix : Since i completed the lore/plot finally trust me (not written but thought of) I needed to change the environement a bit kjust to match the vibe i am going for now! it is now a whitish dusk and not pitch black. ALso i think i fixed the jitter thing. Lapsed (v0.1.4(J)-alpha) Player Movement Rework: Crouching (8) : I have completed reworking the player movement for now– next up ill actually focus ont he core gameplay. In this comit/lapse i have added a crouch functionaility and fixed some other logical stuff that im not going deep into Lapsed (v0.1.4(I)-alpha) Player Movement Rework: Area3D (7) : OKsy! so i and @seb figured out a way to fix the issue i talked about in the previous commit/lapse/. The fix was pretty neat! i added an area 3d on the stairs and the smoothing only work iin this area! i also turned off jumping here Lapsed (v0.1.4(H)-alpha) Player Movement Rework: Smoothness (6) : Fixed the erm “smoothing problem” Previously it was hella snappy and awful to look at - now I added a smoothing thing in the _process but uhhh another problem now – the jumping feels floaty and walk is smoothed out :/ Lapsed Next Step(s): Watch Tower Model Watch Tower Texture Auto Jump on Stairs Core Gameplay Rewriting the todos & story (final istg) Notes: Finally thought of a cohesive plot. I do not have much time or energy to make another chapter – so ive decided to merge everything into one gameplay – the prologue. So yeah! we’ll see how it turns out (i have high hopes tho)
(v0.2.5-alpha) Memory/Vestige Chains (Prototype) Changelog: (v0.2.5-alpha) Vestige Chain (4) : Les goo i completed the full chain of vestiges currently they are just cubes but i will def make models for them later! Lapsed (v0.2.4-alpha) Placement + Vestige Chain (3) : Ok i placed all of the vestiges and currently made the animation till the Vestige 4 – 4 moire to go (counting the watchtower). Idk i think i did some other stuff that i prolly forgot lmfao. Lapsed (v0.2.3-alpha) Particle Fix + Vestige Chain (2) : So previously there was this thing that the particles were all dark and glomy cuz of the exposure thing- Fixed that in this lapse/commit. Also i added the 2nd vestige prototype! Lapsed (v0.2.2-alpha) Vestige Chain: Starting (1) : Yay so i started the next priority in the todo list! I started the vestige chain using only a cube lmao! i will model them later – this is the prototype phase Lapsed (v0.2.1-alpha) Vestiges brain storming : So previously there was this thing that the particles were all dark and glomy cuz of the exposure thing- Fixed that in this lapse/commit. Also i added the 2nd vestige prototype! (v0.1.6.3-alpha) Todo Cleanup : Yep so the thing was that I was too disorganized and too overwhelmed. I sat down today and wrote down everything I think i have a clear picture of what I want to do now! (v0.1.6.2-alpha) Rename & Bunker Prototype + Optimization : I renamed everything. I think i said yesterday that The story is now one single cohesive storyline so I renamed everything to match the new thing. Also I made a hole in the ground (for the bunker) and added a mesh just to visualize it lol. Lapsed (v0.1.6.1-alpha) Story + Optimizations : Right so as i mentioned yesterday i had finalized the plot, in this commit/lapse I wrote it down! Oh also I did something that Ive kept in my todo for a long time lol – I optimized the flower and grass models Next Step(s): Rewriting the todos & story Prototype Vestige Chain Bunker prototype Notes: Wow im speeding up here! i actually finished the entire vestiges sequence Next up imma prototype the bunker
(v0.3.3-alpha) Bunker & Interaction Changelog: (v0.3.3-alpha) Player Interaction (2) : Me add raycast to the face of player, poke poke thing, thing cry, raycat know what it did, raycast say name, i hear, i do something; In a nutshell the raycast detects the collider and if its the bench it enables the e to interact thing Lapsed (v0.3.2-alpha) Player Interaction (1) : Set up the basic of the player interaction– ill work on it more tmrw Lapsed (v0.3.1-alpha) Map Positioning (Ending) : Positioned the map so that the dead space is directly below the drop chute- and other fixes and tests Lapsed (v0.2.13-alpha) Bunker: More models & logics (7) : Improved model to include a turn and a drop, finally added more mechanics and animations. The bunker now dissapears if u look away. fixed some other logics Lapsed (v0.2.12-alpha) Bunker: Mechanics; Showing Maps (6) : When player enters the doors closes and then the original map dissapears, you keep walking forward and the secondary map is spawned. As u come out of the other side the tunnel leads you to the secondary map. Lapsed (v0.2.11-alpha) Bunker Prototype: Door Mechanics & Stairs (5) : made the door open and close animations actually happen with the players actions. Also added an AREA3D which allows the stair logic to kick in. Added the map thing but will animation and add the mechanics in the next update! Lapsed (v0.2.10-alpha) Bunker Prototype: Model & Animations (4) : experimented with another map and stuff. Modelled the top portion and added a simple animation for the doors Lapsed (v0.2.9-alpha) Bunker Prototype: Model (3) : completed the bunker modelling – mostly protype rn but yeah! Next up i think imma make the mechanism of the bunker mm Lapsed (v0.2.8-alpha) Weeping Silhouette: Animation (2) : Les goo i finally completed the weeping silhouette thing! Now it explodeds kinda when u go near it and leaves the player stranded they player has to navigate their way into the next parts! Lapsed (v0.2.7-alpha) Weeping Silhouette (1) : modelled the weeping silhouette and placed it where its supposed to- in the next update I’ll add the animations and stuff! Lapsed (v0.2.6-alpha) Bunker Prototype (1+2) : Well i tried making the bunker prototype but erm i didnt realise how tuff this is gonna be skull. I will try again cuz Im so sick to think straight- ill try to do some other thing toda Lapsed Next Step(s): Bunker prototype Ending Bench interaction Notes: Ive almost completed the FULL game sequence! like ik its using prototype things but the core fucntionality is almost done! super excited
(v0.4.1-beta) Ending, Credits n Modeling Changelog: (v0.4.1-beta) Bunker/Tunnel Model & Texturing) : Oh finally! I am finished with the alpha versions and i moved on to the beta stage! In this session I completed modelling and texturing the bunker as well as the tunnel models! Lapsed (v0.3.9-alpha) Placement, Playtesting & Exporting : Oh my! I am really finished with alpha versions! I will be now moving on to the beta stage. In this session I placed trees, playtested and exported for the alpha release. Lapsed (v0.3.8-alpha) Credits (3) : Yippie i completed the credit scene although the “press anmything to conitnue” doesnt work as of now– but ill add it on my todo for later! Lapsed (v0.3.7-alpha) Credits (2) : THe second lapse/commit of me workin on the credits! I am ALMOSt complete i js need one more session to finalize some stuff Lapsed (v0.3.6-alpha) Bug Fixes & Credits (1) : Fixed an annoying bug where the top of the watch tower was affected by the stair aread3d. Now an invisible wall prevents jumping. Also Added the basic Credits scene. Lapsed (v0.3.5-alpha) Bench Camera Animation, Interaction (2,3) : Aha! I think i am really done with the ending animations! Now the E to interact only works when u are looking directly at the bench. I think i might change the blinking animation tho- Lapsed (v0.3.4-alpha) Bench Camera Animation (1) : Right now I’m Adding the animation and controls for the bench interaction and the cut scene. Added some blinking thing and location changes and movement- Will make the cutscene next! Lapsed Next Step(s): Ending Bench interaction Bunker and Tunnel Models Watch Tower Furniture Vestiges Notes: Yesterday I released the alpha version! the 0.3.9, for win, mac as well as linux! I completed my first priority things and i have moved on to the beta!
(v0.4.11-beta) Lantern, & VESTIGESSS Changelog: (v0.4.11-beta) Vestige 7 Model) : FINALLY FIALTLTJkdas I completed all the vestiges! The final one was the mirror and the chair. Im so tried cya Lapsed (v0.4.10-beta) Vestige 6 Model : Made another model– the wheel chair i am trying to complete this first so ill add the more niche things like umbrellas later Lapsed (v0.4.9-beta) Vestige 5 Model : Okay fifth one down! TWO MOREW i repeat 2 MORE- i wanna complete them asap but lets see! This was a pretty challenging one dawg- the piano was insane BUT it looks so good now Lapsed (v0.4.8-beta) Vestige 4 Model : Made the fourth model– the table, chair, cups and the candles! next up is the musical item- idk how i will make the piano but oh well lets see! Lapsed (v0.4.7-beta) Vestige 3 Model (1) : Erm this is the 3rd model that im doing- ik the uv will be fked uop but oh well. Made the tent, wooden logs and the camfire Lapsed (v0.4.6-beta) Vestige 2 Model : Oh god it is so late but yet im pushing it- I did like 30 more minutes im going to eep now- Anyways in this session i made the swing! Only model tho Lapsed (v0.4.5-beta) Vestige 1 Modelling : In this session I just modelled the crib and the teddy as fast as i could- I need to ramp up my speed here- I dont got much time Lapsed (v0.4.4-beta) Hanging Lanterns & Placement : Modelled the hanging lantern and placed it around the map especially the bunker porttions Lapsed (v0.4.3-beta) Lantern Model : In about 2-ish hours i cooked up this absolutely BEAUTIFUL and awesome lantern model. I just have no words– its just perfect! also added some particle effects and yeah! looks pretty cool Lapsed (v0.4.2-beta) Vestige: Pointer : A point at the center of the screen. yes Lapsed Next Step(s): Ending Bench interaction Bunker and Tunnel Models Watch Tower Furniture Vestiges Vestiges animations Notes: Im so so so close to completing this! so I need pick up the pace here! Completed all the vestiges here
(v0.5.4-beta) VESTIGES ARE ALIVE! Changelog: (v0.5.4-beta) Vestige 7 Animation : Les goo! another task checked off from the todo list! Completed all the animations of the vestiges that forms the backbone of the game- This was the final one (on the path). Next lwk i think ill do sound design maybe Lapsed (v0.5.3-beta) Vestige 6 Animation : I got stuck in this session cuz of a simple bug :skul what happened was that the animation player in the wheelchair scene was changing the position of the root and so it messed everything up- Took me a while to solve it lol. Added some movement and wheel rotation. Lapsed (v0.5.2-beta) Vestiges 3,4,5 Animation : I thought i would complete making the animations today but clearly I failed- Oh well I fixed a LOT of things and FULY completete the third forth and fifth vestiges Lapsed (v0.5.1-beta) Vestiges 1,2,3 Animation : Since I completed modelling and texturing the vestiges the only logical next step is to incorporate them into the game, and thats what ive been doing! Integrated the first 3 vestiges and added some simple animations associated w em Lapsed (v0.4.14-beta) Vestige 4,5,6,7 Texturing : Oh my god- Finished all ALL the texturing- I really wanted thisto be finished faster lol. Anyways in the next session I would make the animations and placements of the vestiges! Lapsed (v0.4.13-beta) Vestige 1,2,3 Texturing : Completed texturing vestiges 1, 2 and 3- i.e. the crib the swing and the tent. Were a lot of fun to see the white models come to life lol. I really like how they are turning out Lapsed (v0.4.12-beta) Models Finalizing : Last day I completed modelling all the vestiges and now only the texturing is left. In this session I finalized the models and meshes and next ill texture them! Lapsed Next Step(s): Vestiges Vestiges animations Watch Tower Furniture Sound Effects Notes: Yippie I completed all the vestiges progression and their associated animations! Idk what ill work on next but maybe sound design? idrk tho...
|
1792760023.0626469
|
1793364823.0626469
|