Max can't build table using jsui ?

Konstantin Popov's icon

Hello everyone,
I'm currently exploring alternatives to jit.cellblock for building a table UI, since I've noticed performance issues when handling larger datasets.
I assumed jsui could be used to create a custom table, but so far I haven’t had any success.
Each time I insert my code (thanks to chatgpt) into the jsui object, the result is just a blank white canvas — no visible grid, rows, or columns.
Has anyone encountered this before or knows what might be causing it? Here's an example of my code. Thanks in advance for any help!

autowatch = 1;

var rows = 100;
var cols = 10;
var rowHeight = 20;
var colWidth = 80;
var data = [];

for (var r = 0; r < rows; r++) {
    var row = [];
    for (var c = 0; c < cols; c++) {
        row.push("R" + r + "C" + c);
    }
    data.push(row);
}

function paint() {
    with (sketch) {
        glclearcolor(1, 1, 1, 1);
        glclear();

        var visibleRows = Math.floor(box.rect[3] / rowHeight);

        for (var r = 0; r < visibleRows; r++) {
            var dataRow = r;
            if (dataRow >= rows) break;

            for (var c = 0; c < cols; c++) {
                var x = c * colWidth;
                var y = r * rowHeight;

                moveto(x + 5, y + 15);
                textcolor(0, 0, 0, 1);
                text(data[dataRow][c]);
            }
        }

        stroke();
    }
    refresh();
}

TFL's icon

I suck at the Sketch API and can't say why your code doesn't work, but turns out I was facing a similar situation and I just ended up building my table in an html page with some of the fancy javascript API existing for this purpose, and display it in my patch through [jweb].

I found Handsontable quite easy to use and giving slick results quickly, but I ended up using Tabulator, which feels more clunky in terms of UI/UX and has a very not well organized doc in my opinion, but has a key feature that was missing from handsontable and that I couldn't manage to implement myself (lookup tables to decorrelate what you see on screen VS what is actually stored in the database).

LLM will most likely be more fluent with building an html page with these than writing code for jsui.

You can even tell ChatGPT or Claude that you want to build a webpage using one of these tools with the purpose of using it in MaxMSP through jweb , and they will actually understand quite well what the window.max commands (to bind inlet messages, send from outlet, get and set Max dictionnaries) are for in this context.

If you just want to display and explore a table with a simple structure, you should be fine. Editing can is a small step above, but not too complicated either. Things quickly escalade if you want grouped columns, custom rules to enter data, etc.

Joshua Kit Clayton's icon

Btw, the "AI Slop" :) above is close but mixes up Sketch and MGraphics APIs. In general I would recommend using the MGraphics API for any 2D rendering as it will typically be higher quality and better suited for multiple views, compositing with other objects, presentation mode, etc.

Generally, if you are going to iterate with AI for this, you will likely want to make clear that you want to use the MGraphics API, and with all AI generated code, you will definitely need to check for errors (I personally don't want to spend my time reviewing people's AI generated code for errors and probably will avoid follow ups in the future on this sort of thing).

I would also second TFL's advice, when consulting AI Models. It is likely going to be more fruitful to make use of jweb, nodescript, or other common web related technologies for which AI models have much greater training expertise.

That said, in case the example is helpful for you or others, here's an example JSUI for such tabular data using mgraphics and theme compatible colors. Now that this example is on the internet maybe the models will learn from it ;)

mgraphics.init();
mgraphics.autofill = 0;
mgraphics.relative_coords = 0;

var rows = 100;
var cols = 10;
var rowHeight = 20;
var colWidth = 80;
var data = [];

for (var r = 0; r < rows; r++) {
    var row = [];
    for (var c = 0; c < cols; c++) {
        row.push("R" + r + "C" + c);
    }
    data.push(row);
}

function paint() {
	var viewsize = mgraphics.size;
	var width = viewsize[0];
	var height = viewsize[1];
	
	mgraphics.set_source_rgba(this.patcher.bgcolor);
	mgraphics.rectangle(0, 0, width, height);
	mgraphics.fill();
	
	mgraphics.set_source_rgba(this.patcher.color);
	mgraphics.font = "Arial";
	mgraphics.fontsize = 10;
	
	var visibleRows = Math.floor(box.rect[3] / rowHeight);
	for (var r = 0; r < visibleRows; r++) {
		var dataRow = r;
		if (dataRow >= rows) break;

		for (var c = 0; c < cols; c++) {
			var x = c * colWidth;
			var y = r * rowHeight;
			
			mgraphics.move_to((x + 5), (y + 15));
			mgraphics.show_text(data[dataRow][c]);
		}
	}
}

function bang()
{
	mgraphics.redraw();
}