For details, see How to Use.
Simple Tool for Animating Sequential Images
This program is a lightweight tool developed with VCSSL that allows you to play back sequential image files (in PNG or JPEG format) as an animation without converting them into a video file.
Here, "sequential image files" refers to image files with filenames that include consecutive numbers at the end, such as: image1.png, image2.png, image3.png, ..., image100.png.
How to Use
Download and Extract
At first, click the "Download" button at the above of the title of this page by your PC (not smartphone). A ZIP file will be downloaded.
Then, please extract the ZIP file. On general environment (Windows®, major Linux distributions, etc.), you can extract the ZIP file by selecting "Extract All" and so on from right-clicking menu.
» If the extraction of the downloaded ZIP file is stopped with security warning messages...
Execute this Program
Next, open the extracted folder and execute this VCSSL program.
For Windows
Double-click the following batch file to execute:
For Linux, etc.
Execute "VCSSL.jar" on the command-line terminal as follows:
java -jar VCSSL.jar
» If the error message about non-availability of "java" command is output...
If You Run Out of Memory While Running the Program...
Depending on how you use it, this program may consume a considerable amount of memory. As a result, the default memory allocation may not be sufficient in some cases. If you encounter such issues, please refer to the following:
» When You Run Out of Memory While Running the Program (for Microsoft® Windows® Users)
» When You Run Out of Memory While Running the Program (for Linux® and Other OS Users
Selecting the Input Folder
Next, you'll be asked whether you want to specify the input folder (i.e., the folder containing the sequential image files you want to animate). If you do want to specify a folder, click "Yes", then choose the location of the folder.
If you click "No" to skip this step, the tool will automatically use the folder named "sample" inside the folder where the program was extracted or downloaded. This folder contains sample sequential images, so if this is your first time trying out the tool, it’s recommended to click "No" here.

Entering the File Name
You will then be prompted to enter the file name.

In the file name input window, you only need to enter the part of the filename excluding the sequential number and extension. For example, if your image files are named like "image123.png", enter just the "image" part.
Selecting the File Extension
Next, you'll be asked to select the file extension. Choose either ".png" for PNG format or ".jpg" for JPEG format. As of now, these are the only two formats supported by this tool.

Choosing Whether to Buffer Image Data
Finally, you'll be asked whether you want to buffer the image data. Unless you encounter memory-related errors, it's generally recommended to select "Yes".

Buffering affects how and when image files are read.
When buffering is enabled, all image data is loaded into memory at the beginning, and the animation is played using that data.
When buffering is disabled, image files are loaded one by one as needed, and each image is discarded from memory after it is shown.
Buffering typically results in smoother animations (especially on the first loop). It also reduces stress on storage devices, which is useful if you're running the animation repeatedly on a machine with an older HDD.
However, buffering requires more memory, so if the images are large or numerous, it may cause the program to terminate due to insufficient memory. In such cases, disabling buffering can help conserve memory usage.
Using the Image Viewer Window
Once all inputs are completed, an image viewer window will appear, and the animation playback will begin automatically.

The controls are similar to those of a standard video player.
You can toggle playback using the "PLAY / STOP" button in the lower-left corner, and use the seek bar (the long slider at the bottom) to jump to any point in the animation.
In the "Image Interval (ms)" field, you can specify the delay (in milliseconds) between one image and the next. Larger values result in slower playback speed.
Code
This program is written in VCSSL.
If you want to modify or customize the tool, you can simply open the program file "ImageFileAnimator.vcssl" in a text editor and make changes directly.
Since VCSSL is a scripting language, no recompilation is required. Its syntax is simple and C-like, so if you have any experience with C or similar languages, you should be able to understand most of it intuitively.
Entire Code
This is more of a practical tool than just a sample script, so the code is a bit lengthy and includes some ad-hoc handling in various parts. Because of that, instead of explaining the code line by line from the top, we'll simply provide the full source code here.
If you'd like to understand how it works in detail, it's recommended to start reading from the main function and follow the logic step by step. Refer to the comments in the code as needed, and dive into the lower-level functions along the way.
Please note that this is a standalone, quick-and-dirty tool, so there are quite a few hardcoded values throughout the script-including parts of the GUI layout. We hope you'll forgive the lack of polish in those areas.
Here is the complete source code of the tool:
coding UTF-8; // Specify character encoding (to prevent mojibake)
// Import libraries
import GUI; // Library for GUI components like windows and buttons
import Graphics; // Library for handling image data and graphic resources
import Graphics2D; // Library for 2D rendering
import File; // Library for handling file and folder information
import system.Int; // Library providing helper functions for integers
// Constant settings for GUI
const int DEFAULT_WINDOW_WIDTH = 800; // Initial window width
const int DEFAULT_WINDOW_HEIGHT = 700; // Initial window height
const int CONTROL_AREA_HEIGHT = 60; // Height of the control section at the bottom of the window
const int PLAY_BUTTON_WIDTH = 140; // Width of the play/stop button
const string PLAY_TEXT = "PLAY"; // Label text for the play button
const string STOP_TEXT = "STOP"; // Label text for the stop button
const int BACKGROUND_COLOR[] = { 50, 50, 50, 255 }; // Background color (R,G,B,Alpha)
// Constant settings for file paths
const string DEFAULT_DIRECTORY_PATH = "./sample"; // Default folder path
const string DEFAULT_FILE_NAME = "image"; // Default image file name (excluding number and extension)
const string FILE_EXTENSION_SELECT_MESSAGE = "- Select Extension -"; // Message shown when selecting file extension
const string FILE_EXTENSIONS[] = { FILE_EXTENSION_SELECT_MESSAGE, ".png", ".jpg" }; // Extension options
// Other constant settings
const int DEFAULT_WAIT = 30; // Default interval between frames (milliseconds)
const int FILE_NUMBER_SEARCH_LIMIT = 1000; // Max number to search for start of sequence
// Variables related to file names and paths (set in the configure function)
string directoryPath; // Path to the folder containing image files
string fileNameHead; // File name prefix (excluding number and extension)
string extension; // File extension (including dot)
int fileNumberBegin; // Starting number of the sequence
int fileNumberEnd; // Ending number of the sequence
// Variables related to GUI layout
int windowWidth = DEFAULT_WINDOW_WIDTH; // Width of the image display window (can change)
int windowHeight = DEFAULT_WINDOW_HEIGHT; // Height of the image display window (can change)
int screenWidth; // Width of the display screen (set in the "configure" function)
int screenHeight; // Height of the display screen (set in the "configure" function)
// Variables for GUI components
int screenWindow = NULL; // ID of the image display window
int screenImageLabel = NULL; // ID of the image display label
int screenGraphics = NULL; // ID of the graphics resource for drawing on the screen
int screenRenderer = NULL; // ID of the renderer (drawing engine) for the screen
int playButton = NULL; // ID of the play/stop button
int seekSlider = NULL; // ID of the seek bar
int waitField = NULL; // ID of the field for setting frame interval
int seekLabel = NULL; // ID of the label next to the seek bar
int waitLabel = NULL; // ID of the label next to the interval input field
// Variables related to images
int totalNumberOfImages; // Total number of image files
string imageFilePaths[]; // Array to store paths of all image files
int imageGraphicsBuffers[]; // Array to buffer image contents (stores graphics resource IDs for each image)
// Variables for control
bool continuesMainLoop = true; // Controls whether the main loop continues (set to false to exit)
bool windowSizeChanged = false; // Set to true when the window is resized, triggers re-layout
bool playing = true; // True when playing, false when stopped
bool bufferingEnabled; // Controls whether image buffering is enabled (set in the configure function)
// ============================================================
// Main process - This function runs automatically on startup
// ============================================================
void main() {
// Perform configuration based on user input
configure();
// Initialize drawing-related resources
initializeGraphicsResources();
// Initialize GUI components
initializeGUI();
// Apply layout settings to the GUI
updateLayout();
// Once the image window is open, hide the CUI console window to avoid clutter
hide();
// Variable to store the last displayed image index, to avoid redundant redraws
int lastImageIndex = -1;
// Main loop: updates and redraws the image on each cycle
while (continuesMainLoop) {
// Get the image index to display, based on the seek bar's position
int imageIndex = getComponentInt(seekSlider);
// Draw the image on the screen (skip if the image hasn't changed)
if (imageIndex != lastImageIndex) {
drawScreen(imageIndex);
}
// Handle window resize
if (windowSizeChanged) { // This variable is set to true in "onWindowResize" when a resize is detected
updateLayout(); // Reapply GUI layout
drawScreen(imageIndex); // Redraw image on screen
windowSizeChanged = false; // Reset resize flag
}
// Store the index of the last drawn image
lastImageIndex = imageIndex;
// Wait for the specified interval before the next frame (animation delay)
int wait = getWaitValue();
sleep(wait);
// Advance time if animation is playing
if (playing) {
// Increment image index; loop back to start if at the end
imageIndex++;
if (imageIndex == totalNumberOfImages) {
imageIndex = 0;
}
// Update the seek bar to match the new image index
setComponentInt(seekSlider, imageIndex);
}
}
// Exit the program when the main loop ends
exit();
}
// ==================================================
// Function for handling configuration via user input
// ==================================================
void configure() {
// Select the folder to load image files from (if skipped, the "sample" folder is used)
directoryPath = DEFAULT_DIRECTORY_PATH;
if (confirm("Do you want to specify the folder for loading images?", "(If you select 'No', the '" + DEFAULT_DIRECTORY_PATH + "' folder will be used)")) {
directoryPath = choose("Select the folder to load images from", ".");
}
println("Image folder = " + directoryPath);
// Enter the file name (excluding number and extension)
fileNameHead = input("Enter the image file name (excluding number and extension)", DEFAULT_FILE_NAME);
println("Image file name = " + fileNameHead);
// Select the file extension
extension = select(FILE_EXTENSIONS);
while (extension == FILE_EXTENSION_SELECT_MESSAGE) {
extension = select(FILE_EXTENSIONS);
}
println("Extension = " + extension);
// Search for image files in the folder and determine the start and end of the sequence
scanFileIndex();
// Choose whether to enable image buffering
bufferingEnabled = confirm(
"Enable image buffering?",
"Buffering helps animations play more smoothly.",
"It also reduces stress on HDDs, especially on older PCs.",
"However, buffering uses more memory, which may cause issues in low-memory environments."
);
}
// ==================================================================================
// Function to search for image files and determine the start and end of the sequence
// ==================================================================================
void scanFileIndex() {
println("Searching for files...");
// Search for the starting number and set it to "fileNumberBegin"
fileNumberBegin = 0;
while (true) {
// Starting from 0, increment fileNumberBegin by 1 and
// check if a file with that number exists;
// the first one found will be considered the start of the sequence
string fileName = fileNameHead + fileNumberBegin + extension;
string filePath = getFilePath(fileName, directoryPath);
if (exists(filePath)) {
println("Start file: " + filePath);
break;
}
if(fileNumberBegin >= FILE_NUMBER_SEARCH_LIMIT) {
popup("Searched up to file number " + FILE_NUMBER_SEARCH_LIMIT + " but no sequential file was found.");
exit();
}
fileNumberBegin++;
}
println("Start number = " + fileNumberBegin);
// Search for the ending number and set it to fileNumberEnd
fileNumberEnd = fileNumberBegin;
while (true) {
// Starting from fileNumberBegin, increment fileNumberEnd by 1
// and check if a file with that number exists;
// when no such file is found, set (current number - 1) as the end of the sequence
string fileName = fileNameHead + fileNumberEnd + extension;
string filePath = getFilePath(fileName, directoryPath);
if (!exists(filePath)) {
fileNumberEnd--;
break;
}
fileNumberEnd++;
}
println("End number = " + fileNumberEnd);
}
// ==================================================
// Initialization for rendering-related resources
// ==================================================
void initializeGraphicsResources() {
// Calculate and set the total number of image files
totalNumberOfImages = fileNumberEnd - fileNumberBegin + 1;
// Allocate arrays for file paths and buffers, using the total image count
alloc(imageFilePaths, totalNumberOfImages);
alloc(imageGraphicsBuffers, totalNumberOfImages);
// Store the full file paths in the array
for (int i=0; i<totalNumberOfImages; i++) {
string fileName = fileNameHead + (fileNumberBegin + i) + extension;
imageFilePaths[i] = getFilePath(fileName, directoryPath);
}
// If buffering is enabled,
// load all image files in advance and create graphics resources,
// then store them in the buffer array
if (bufferingEnabled) {
for (int i=0; i<totalNumberOfImages; i++) {
println("Preparing image buffer: " + imageFilePaths[i]);
imageGraphicsBuffers[i] = newGraphics(imageFilePaths[i]);
}
println("Image buffer preparation complete");
}
// Create the graphics resource and renderer (drawing engine) for the screen
screenGraphics = newGraphics();
screenRenderer = newGraphics2DRenderer(1, 1, screenGraphics); // 幅や高さはupdateLayout関数で再設定
}
// ==================================================
// Initialization for screen (GUI) components
// ==================================================
void initializeGUI() {
// Create the image display window
screenWindow = newWindow(0, 0, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT, "Image File Animator");
// Create the image display label and place it in the window
screenImageLabel = newImageLabel(0, 0, 0, 0, screenGraphics); // Width and height will be updated in updateLayout()
mountComponent(screenImageLabel, screenWindow);
// Create the play/stop button and place it in the window
playButton = newButton(0, 0, 0, 0, STOP_TEXT); // Width, height, and position will be set in updateLayout()
setComponentFontSize(playButton, 20);
mountComponent(playButton, screenWindow);
// Create the seek bar (horizontal slider) and place it in the window
seekSlider = newHorizontalSlider(0, 0, 0, 0, 0, 0, totalNumberOfImages-1); // Same for width, height, and position
mountComponent(seekSlider, screenWindow);
// Create the info label to the left of the seek bar and place it in the window
seekLabel = newTextLabel(0, 0, 0, 0, ""); // Same for width, height, and position
mountComponent(seekLabel, screenWindow);
// Create the interval (wait time) input field and place it in the window
waitField = newTextField(0, 0, 0, 0, (string)DEFAULT_WAIT); // Same for width, height, and position
mountComponent(waitField, screenWindow);
// Create the label next to the interval input field and place it in the window
waitLabel = newTextLabel(0, 0, 0, 0, "Update Interval (ms) ="); // Same for width, height, and position
mountComponent(waitLabel, screenWindow);
}
// =======================================================
// Function to get the user-specified interval (wait time)
// =======================================================
int lastWait = -1; // Store the previous value to avoid duplicate warning messages
int getWaitValue() {
// Get the value from the wait time input field as a string
string waitText = getComponentString(waitField);
// If the input is not a valid integer, wait briefly and try again (in case the user is editing)
while (!isInt(waitText)) {
waitText = getComponentString(waitField);
sleep(100);
}
// If the value starts with 0 and has more than one digit, remove the leading zero
// (to avoid interpretation as old-style octal literals, which are deprecated and may trigger warnings)
while (countText(waitText) >= 2 && checkText(waitText, "0", Text.START)) {
waitText = replaceText(waitText, "0", "", Text.FIRST);
}
// Convert the string to an integer, and show a warning if the value is unusually large
int wait = (int)waitText;
if (wait >= 10000 && wait != lastWait) {
if( !confirm("A large value (10 seconds or more) is specified for the interval. Are you sure you want to apply it?") ) {
waitText = (string)DEFAULT_WAIT;
setComponentText(waitField, waitText);
wait = DEFAULT_WAIT;
}
}
// Store the value that triggered a warning, to skip future warnings for the same value
lastWait = wait;
return wait;
}
// ============================================================
// Function to handle layout of GUI components on the screen
// ============================================================
void updateLayout() {
// Get the inner size of the window and calculate the display area size
int windowInnnerSize[] = getComponentSize(screenWindow, INNER);
screenWidth = windowInnnerSize[0];
screenHeight = windowInnnerSize[1] - CONTROL_AREA_HEIGHT;
// Update the renderer and image label with the new screen size
setGraphics2DSize(screenRenderer, screenWidth, screenHeight);
setComponentSize(screenImageLabel, screenWidth, screenHeight);
// Layout settings for the control panel at the bottom of the window
// Play/stop button
setComponentSize(playButton, PLAY_BUTTON_WIDTH, CONTROL_AREA_HEIGHT);
setComponentLocation(playButton, 0, screenHeight);
// Seek bar
setComponentSize(seekSlider, screenWidth - PLAY_BUTTON_WIDTH - 180, 20);
setComponentLocation(seekSlider, PLAY_BUTTON_WIDTH + 160, screenHeight + 10);
// Wait time input field
setComponentSize(waitField, 100, 20);
setComponentLocation(waitField, PLAY_BUTTON_WIDTH + 160, screenHeight + 30);
// Info label to the left of the seek bar
setComponentSize(seekLabel, 140, 20);
setComponentLocation(seekLabel, PLAY_BUTTON_WIDTH + 20, screenHeight + 10);
// Description label to the left of the wait time input
setComponentSize(waitLabel, 140, 20);
setComponentLocation(waitLabel, PLAY_BUTTON_WIDTH + 20, screenHeight + 30);
// Repaint the labels
paintComponent(seekLabel);
paintComponent(waitLabel);
}
// ==================================================
// Function to draw an image on the screen
// ==================================================
void drawScreen(int i) {
// Declare a variable to store the ID of the graphics resource for the image
int imageFileGraphics;
// If buffering is enabled, use the preloaded graphics resource
if (bufferingEnabled) {
imageFileGraphics = imageGraphicsBuffers[i];
// If buffering is disabled, load the image from the file path and create a graphics resource
} else {
imageFileGraphics = newGraphics(imageFilePaths[i]);
}
// Get the width and height of the image
int imageWidth = getGraphicsWidth(imageFileGraphics);
int imageHeight = getGraphicsHeight(imageFileGraphics);
// Calculate the aspect ratio of the image
float imageAspectRatio = (float)imageWidth / (float)imageHeight;
// Calculate the width and height to draw the image, fitting it to the screen while preserving aspect ratio
int drawWidth = screenHeight * imageAspectRatio;
int drawHeight = screenHeight;
if (drawWidth > screenWidth) {
drawWidth = screenWidth;
drawHeight = screenWidth / imageAspectRatio;
}
// Calculate the top-left corner position to center the image on the screen
int drawX = screenWidth/2 - drawWidth/2;
int drawY = screenHeight/2 - drawHeight/2;
// Fill the screen with the background color
setDrawColor(screenRenderer, BACKGROUND_COLOR);
drawRectangle(screenRenderer, 0, 0, screenWidth, screenHeight, true);
// Draw the image on top of the background
drawImage(screenRenderer, drawX, drawY, drawWidth, drawHeight, imageFileGraphics);
int imageNumber = getComponentInt(seekSlider) + fileNumberBegin;
setComponentString(seekLabel, "Image Number=" + imageNumber);
// Repaint the image display label
paintComponent(screenImageLabel);
// If buffering is disabled, delete the graphics resource after each draw
if (!bufferingEnabled) {
deleteGraphics(imageFileGraphics);
}
}
// ============================================================
// Function called when a button is clicked (event handler)
// ============================================================
void onButtonClick(int id) {
// If the play/stop button is clicked
if (id == playButton) {
// Toggle between playing and stopped states
playing = !playing;
// Update the button label accordingly
if (playing) {
setComponentString(playButton, STOP_TEXT);
} else {
setComponentString(playButton, PLAY_TEXT);
}
}
}
// ============================================================
// Function called when the window is resized (event handler)
// ============================================================
void onWindowResize(int id, int width, int height) {
// If the image display window is resized
if (id == screenWindow) {
// Set the resize flag to true to trigger layout update in the main loop
// (Avoid heavy processing here since resize events may fire repeatedly)
windowSizeChanged = true;
}
}
// ============================================================
// Function called when the window is closed (event handler)
// ============================================================
void onWindowClose (int id) {
// If the image display window is closed
if (id == screenWindow) {
// Exit the main loop and end the program
continuesMainLoop = false;
}
}
ImageFileAnimator.vcssl
That concludes the source code.
License
This VCSSL/Vnano code (files with the ".vcssl" or ".vnano" extensions) is released under the CC0 license, effectively placing it in the public domain. If any sample code in C, C++, or Java is included in this article, it is also released under the same terms. You are free to use, modify, or repurpose it as you wish.
* The distribution folder also includes the VCSSL runtime environment, so you can run the program immediately after downloading.
The license for the runtime is included in the “License” folder.
(In short, it can be used freely for both commercial and non-commercial purposes, but the developers take no responsibility for any consequences arising from its use.)
For details on the files and licenses included in the distribution folder, please refer to "ReadMe.txt".
* The Vnano runtime environment is also available as open-source, so you can embed it in other software if needed. For more information, see here.
Simple Tool for Viewing and Converting Between RGB Values and Color Codes |
|
![]() |
Display and Convert RGB Values and Color Codes on a GUI Tool |
A Simple Tool for Making Specific Colors Transparent (Batch Processing Version) |
|
![]() |
Batch-Convert a Specific Color to Transparency in All PNG Files in a Folder |
A Simple Tool for Making Specific Colors Transparent |
|
![]() |
Convert a Specific Color to Transparency in PNG Image Files |
Simple Tool for Animating Sequential Images |
|
![]() |
A lightweight tool developed with VCSSL that allows you to play back sequential image files as an animation without converting them into a video file. |
Vertex Array-Based Model Deformation Animation |
|
![]() |
How to Deform 3D Surface Models Using a Grid-Based Vertex Array |
Creating a Model from a Vertex Array (Quadrangle Grid Mesh Format) |
|
![]() |
How to Create 3D Surface Models from a Grid-Based Vertex Array |