#Bad Apple!! but it's implemented using KubeJS' Painter API
62 messages · Page 1 of 1 (latest)
there's no fucking way
i thought i saw it all...

Ok hello yes I’m back time for some promised
✨Technical detail ✨
Basic layout
The way this works is simple:
- Download the bad apple video from youtube
- Use a python script to split the video into frames, convert them to pure black-or-white, and encode them
- The encoding works by tracking changes for each pixel between the current frame and the previous, and then adding its x/y position to an array. So if a pixel was white a frame ago and now it’s black, it will get added; if it was white and stays white, nothing happens. Since the video only contains 2 colors this information is enough to then reconstruct the video in kjs
- Encode all frames and add them to a single array, then dump that array into a json file (then realize kjs jsonio has a bug which only allows it to read map objects from json files, so then instead of writing the array directly first add it to a dict and dump that instead)
- Read the encoded video json from a kjs startup script and write the result to
global(this is done so that the game wouldn’t load the file each time a /reload occurs, causing an OutOfMemory error) - Draw multiple black rectangles using painter, each one representing a single pixel of the video. Name them "{x position of the pixel}x{y position of the pixel}". Get the size of the client’s screen by sending a packet to the player and getting a response, and then based on that info scale the "pixels" accordingly
- Loop through every single frame and flip the color of the encoded pixel positions. Make sure to wait a bit before rendering each frame to make the final sequence not go apeshit fast on the screen
- Attach the activation of this animation to a custom command. Run that command in game, record the video, edit it a bit and profit! You’re done
Technical limitations
As said previously, this type of encoding (btw idk if it has a name I came up with it myself lmao) only allows for purely 2-colored animations, although you could probably do some jank shit like attaching the hex/palette representation of a pixel’s new color as the third element in each encoded pixel’s array or do some elaborate stuff with pixel regions and shit, but you can probably tell I cba 
When I originally tried this project a couple of months ago I tried to use RLE as an encoding until I realized it’s very inefficient and that I had to implement it in JS didn’t help things
. The new encoding does trade the freedom RLE can give you for its speed but since this is bad apple we’re talking about this doesn’t really matter.
A sane human being might also say that painter can draw textures, and that I should’ve instead just loaded the split frames directly into the game and drawn those instead. That would be more reasonable but I think by now you should’ve already guessed that adjective doesn’t really apply to me. Ig this way you can also display video with color but shut up idc.
As also said previously I had to speed up the video in post by about ~158%. This is because you can’t really do anything in mc sub-frame and there is only 20 frames in a second, whereas my original video is 30 fps. There are only 2 solutions to this problem: I could either render all of the frames sequentially (as mentioned before, the speed would go apeshit) or do what I did and render them a bit slower then in the actual video. As you might’ve also noticed the reference video and the actual in-game footage are not synced sometimes, this is partially due to this and also because it takes a bit of time to actually render the frame, not a lot but enough to cause desync. Audio was also added in editing, you can probably guess why. 
The only other technical limitation I encountered is davinci resolve on linux go fuck yourself black magic and you shitty fucking encoding support (respectfully). The final .mov file of the video you’re seeing weighed 7 FUCKING GIGABYTES. After I converted it back to an .mp4 it only weighs 33 megs. (ty apple as well love your uncompressed formats)
The bug
One of the most critical bugs I’ve encountered during this is the final animation only showing the outlines for the pixels that have changed. I’ve spend a good 2 hours trying to figure out what went wrong and finally figured out that the problem was in encodings themselves (and not because of painter weirdness).
Turns out in my generation script, I had a for loop that looped over all of the frames except for the first one which was an artificially added purely black frame (this was done so that the first frame (which is completely black in bad apple btw) when encoded produced an empty array), and to check the pixel in the previous frame, I got that frame by getting the element indexed one below the current one in the frames array.
Because of that one frame being missing from the array I was looping over though, I was actually getting the frame 2 frames back, which lead to these unexpected results and wasted precious hours of my life.
Moral of the story: me dumb
Stuff used
- Video (downloaded with yt-dlp, 144p, 30fps, no audio (downloaded separately when I added it in post))
- The python script (will send it tmrw, on mobile rn and the script is locally on my pc)
- The final encoding (only weighs about 67MB, which is a far cry from 110MB I was getting before I found "the bug")
- All of the kjs scripts are available on the package repo, check it out (or don’t)
Special thanks
I’d like to thank all of the boosters for boosting this server which allowed me to upload the video directly to here instead of youtube. I also would like to thank my mom AND dad obviously. I would NOT like to thank davinci resolve
Sorry if this is a bit cluttered it’s almost 3am and I want to eep
Ok thank you for reading (or skipping to the bottom) bye!!!!11
thanks for calling me out for skipping to the bottom 
Knew it
Uh so yeah last time I checked this is peak painter you can’t do much more with the api so uhhh yeah
This is gonna revolutionize modded minecraft frfr
@half walrus I didn’t see what you deleted
Lmao
@covert gazelle your counters fucking broke btw
oopsies
i love it
wait it works for other packages
so the runner is at fault ig
ah ik what happened
it checks if the package exists in the db and if it doesn't, ignores the request
cba to fix it rn doe
absolute legend
cutscenejs
why not 
because if it exists, it can play bad apple
and I won't sleep until that has been proven 
uh yeah join me next time when I run doom on painter
the promised python script (in case you want to encode your own black and white video and play it using this for some reason)
don't forget to change the name of the file and if you are using a higher def video also change that in the kjs scripts themselves
Paste version of gen.py from @fathom tendon
requires pillow and opencv-python
I mean it's technically possible
I think I'm just not qualified enough to do smth like this
if that's not a thing, then Im probably making that
for guijs or whatever u were gonna call it?
oh yea talking about that.. any progress?
not currently, im working on ichor
fair
quentin did smth like that back in the day iirc
yeah
dunno how good it is 
im.. going to guess not as good as mine 
WTH? Now I've seen everything
all the boosters that boosted the server
so that would be lat.. lat... lat and lat
https://i.latvian.dev/pc/2022-11-15_16.18.01.png (<#off-topic message>)
