Skip to content

The Stability of Aseprite's Scripting Feature

haloflooder edited this page Aug 30, 2018 · 3 revisions

How Stable is Aseprite's Scripting Feature?

Aseprite's scripting feature is unstable depending on what you write in a script. I do understand that Aseprite's scripting feature is still a experimental thing and it's still a WIP for the most part. As of Aseprite version 1.2.9, it uses Javascript for the scripting but they already switched the scripting language to LUA for the next version.

Inefficient code in the script will make Aseprite run slow but efficient code can also make Aseprite run slow as well. The speed really depends on what you're doing in the script. Getting pixels from an image is pretty fast but placing down pixels can be slow depending on what you're doing. There are wonky ways to fix how slow the script will run.

Demonstrating How Stable Scripts Are

This script HERE have many examples of what you can do with Aseprite scripts. It also serves as a document for the Aseprite scripting API as well. DO NOT USE THIS SCRIPT ON YOUR EXISTING PROJECT. Some functions in the script can crash Aseprite since there is one a bug with images. I am not responsible for whatever happens to your project.

Bugs with Images

Currently, there is a very unstable bug with the image api. If you create a new empty document with a size of 128x128. The image size will show as 128x128. If you draw a a single pixel on it, the image size will be 1x1. Same thing applies when you draw a few pixels on the screen like in the image below.

If you use a script to draw outside of the image, Aseprite will crash. The crashing issue would've been easily preventable if there was an X and Y property with an image but there currently isn't one. This issue alone makes the scripting feature extremely unstable if you were to use scripting to manipulate images. There is currently no workaround to this problem to my knowledge.

Weird Things That Speeds Up a Script

I created a function that creates a checkerboard pattern on the image. The initial code was sloppy and inefficient. It took the script 230ms to execute on an image that's 128x128. Keep in mind that all the scripts I will run will be executed on an empty document with the size of 128x128. The speed of the code execution was very slow compared to just completely filling in the image with a solid color.

Show code
function checkerboardVersionOne(selection) {
	// Initialize the required variables for the script
	var dotx = true;
	var doty = true;
	var dotCol = col.rgba(0,0,0,255); // Creates the color black
	var box = selection; // Gets the selection the user selected on the active image
	
	for (var y=box.y; y<box.y+box.height; ++y) {
		for (var x=box.x; x<box.x+box.width; ++x) {
			if (dotx) {
				img.putPixel(x,y, dotCol); // Renders a pixel onto the active image
				dotx = false;
			} else {
				dotx = true;
			}
		}
		if (doty) {
			doty = false;
			dotx = false;
		} else {
			doty = true;
			dotx = true;
		}
	}
	console.log("Executed Checkerboard v1");
}

I cleaned up the code so it's more efficient but the execution time was still about the same.

Show code
function checkerboardVersionTwo(selection) {
	// Initialize the required variables for the script
	var dotCol = col.rgba(0,0,0,255); // Creates the color black
	var box = selection; // Gets the selection the user selected on the active image
	
	for (var y=box.y; y<box.y+box.height; ++y) {
		for (var x=box.x+(y%2); x<box.x+box.width; ++x) {
			img.putPixel(x,y, dotCol); // Renders a pixel onto the active image
			x++;
		}
	}
	console.log("Executed Checkerboard v2");
}

This one was just an experiment to see how fast the code would execute. It obviously looks like it was poorly done but it's just an experiment. The code execution for this code block was only slightly faster than the previous two by only 14ms.

Show code
function checkerboardVersionThree(selection) {
	// Initialize the required variables for the script
	var dotCol = col.rgba(0,0,0,255); // Creates the color black
	var box = selection; // Gets the selection the user selected on the active image

	for (var y=box.y; y<box.y+box.height; ++y) {
		for (var x=box.x; x<box.x+box.width; ++x) {
			img.putPixel(x,y, dotCol); // Renders a pixel onto the active image
			x++;
		}
		y++;
	}
	var axe = box.x+1;
	var why = box.y+1;
	for (var y=why; y<box.y+box.height; ++y) {
		for (var x=axe; x<box.x+box.width; ++x) {
			img.putPixel(x,y, dotCol); // Renders a pixel onto the active image
			x++;
		}
		y++;
	}
	console.log("Executed Checkerboard v3");
}

I was about the give up on the speed but I decided to do something silly. Instead of drawing every other pixel, I decided to draw on every pixel on the image to see what it does. To keep the transparency between each black pixel, I used the image api to get the pixel at the x,y coordinate and re-draw the pixel at the same coordinate to fill in the space.

Show code
function checkerboardVersionFour(selection) {
	// Initialize the required variables for the script
	var dotCol = col.rgba(0,0,0,255); // Creates the color black
	var box = selection; // Gets the selection the user selected on the active image
	
	for (var y=box.y; y<box.y+box.height; ++y) {
		for (var x=box.x; x<box.x+box.width; ++x) {
			if ((x+(y%2))%2 == 0) { // Some math to select every other pixel
				img.putPixel(x,y, dotCol); // Renders a pixel onto the active image
			} else {
				img.putPixel(x,y, img.getPixel(x,y)); // The wonky piece of code that magically fixes how slow the original code was
			}
		}
	}
	console.log("Executed Checkerboard v4");
}

To my surprise, drawing on every pixel did the trick. It took Aseprite 16ms to render the checkerboard pattern which is about 14 times faster than the first iteration of the code. I'm not sure why this fixed the slow script execution. You would think that only drawing half the amount of pixels would be faster, but this was not the case. Here's a gif of the final result.

Minor Bug With Selections

If you make a new selection with a script. Aseprite won't render the new selection onto the screen but the new selection is still valid.
You can’t perform that action at this time.