Bug related to Export\Import System

The code is found here

I found out that if i import the exact same export code on 2 pcs
or even on the same pc but on a different tab
or just reload the same tab and import it
or hit the reload the code \ randomize the outcome with the button

the output art itself looks the same

but the parameters on the ui are different,
In the UI there are 30 Operation Types.
When i load the code, again,
some of the operation types are different than what they were upon saving.

Take this export code for example

{
  "numOperations": 17,
  "operationTypes": [
    3,
    6,
    6,
    6,
    6,
    23,
    21,
    21,
    10,
    22,
    15,
    15,
    15,
    14,
    6,
    6,
    6,
    9,
    20,
    11,
    15,
    10,
    20,
    8,
    6,
    12,
    10,
    6,
    14,
    11
  ],
  "params1": [
    0.3,
    0.3,
    -0.8,
    -1.4,
    -1.1,
    5,
    0.8,
    1.2,
    1.2,
    0.6,
    1.4,
    1.4,
    0.4,
    -1.9,
    0,
    -0.2,
    -0.7,
    0.2,
    1.6,
    1.4,
    -0.7,
    0.4,
    0.7,
    0.4,
    0.9,
    -1.5,
    1.7,
    0.3,
    -2.3,
    0.3
  ],
  "params2": [
    0,
    0,
    0.2,
    1.5,
    -0.4,
    0,
    0,
    0,
    1.2,
    0,
    -0.6,
    0.2,
    0,
    3.1,
    -0.1,
    0.4,
    0.6,
    0.5,
    1.7,
    2,
    -1.4,
    1.5,
    1.4,
    1.4,
    0,
    -3,
    1,
    0.7,
    -2.9,
    1.8
  ],
  "params3": [
    0,
    0,
    -0.6,
    -1.1,
    0.9,
    0,
    0,
    0,
    1.5,
    0,
    0.4,
    1.4,
    0.6,
    3.1,
    0.2,
    0.4,
    -0.6,
    0.9,
    1.8,
    -1.4,
    0,
    0.6,
    1.4,
    0.8,
    0,
    3.1,
    1.2,
    0.9,
    -0.1,
    -1.4
  ],
  "params4": [
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0.74,
    0.75,
    0.76,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0.6,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0
  ],
  "aaEnabled": true,
  "aaLevel": 2,
  "operations": [
    {
      "type": "invertCircle",
      "param1": 0.29663653182990013,
      "param2": 0,
      "param3": 0,
      "param4": 0
    },
    {
      "type": "invertCircle",
      "param1": 0.2582366162026124,
      "param2": 0,
      "param3": 0,
      "param4": 0
    },
    {
      "type": "reflectY",
      "param1": -0.8093457082504134,
      "param2": 0.19984243820134506,
      "param3": -0.6389909201204933,
      "param4": 0
    },
    {
      "type": "rotate",
      "param1": -1.4470424341893329,
      "param2": 1.4999139134193964,
      "param3": -1.0781654670993561,
      "param4": 0
    },
    {
      "type": "reflectY",
      "param1": -1.1118767558867306,
      "param2": -0.449589327652963,
      "param3": 0.9323475535972303,
      "param4": 0
    },
    {
      "type": "brightness",
      "param1": 1.2,
      "param2": 0,
      "param3": 0,
      "param4": 0
    },
    {
      "type": "contrast",
      "param1": 0.8,
      "param2": 0,
      "param3": 0,
      "param4": 0
    },
    {
      "type": "contrast",
      "param1": 1.2,
      "param2": 0,
      "param3": 0,
      "param4": 0
    },
    {
      "type": "mod",
      "param1": 1.212180333387337,
      "param2": 1.1517670573196095,
      "param3": 1.52653811046015,
      "param4": 0
    },
    {
      "type": "saturation",
      "param1": 0.61832618874479,
      "param2": 0,
      "param3": 0,
      "param4": 0
    },
    {
      "type": "noise",
      "param1": 1.4363137099137533,
      "param2": -0.629224533336395,
      "param3": 0.42736104036774525,
      "param4": 0.7434022172010137
    },
    {
      "type": "noise",
      "param1": 1.4211452477788198,
      "param2": 0.2201067531552694,
      "param3": 1.420809235469676,
      "param4": 0.7516100954139769
    },
    {
      "type": "noise",
      "param1": 0.3738551969351087,
      "param2": 0.041597704482951636,
      "param3": 0.5739733007262027,
      "param4": 0.7576145940838706
    },
    {
      "type": "tan",
      "param1": -1.9349276694636974,
      "param2": 3.1246420788538245,
      "param3": 3.103509174556253,
      "param4": 0
    },
    {
      "type": "colorShift",
      "param1": 0.03877021615253495,
      "param2": -0.0805541648774406,
      "param3": 0.16776755847732883,
      "param4": 0
    },
    {
      "type": "colorShift",
      "param1": -0.20987216253145036,
      "param2": 0.448171605594166,
      "param3": 0.35415236760928925,
      "param4": 0
    },
    {
      "type": "affine",
      "param1": -0.6819027975310503,
      "param2": 0.5878704740982656,
      "param3": -0.5955016387736591,
      "param4": 0
    },
    {
      "type": "affine",
      "param1": 0.24412448468074732,
      "param2": 0.4713650589922169,
      "param3": 0.8719589577849298,
      "param4": 0
    },
    {
      "type": "mixColors",
      "param1": 1.5666994887872652,
      "param2": 1.7078580509745174,
      "param3": 1.848585604271515,
      "param4": 0
    },
    {
      "type": "abs",
      "param1": 1.3861171140277913,
      "param2": 1.9992135376173055,
      "param3": -1.3641094864050176,
      "param4": 0
    },
    {
      "type": "noise",
      "param1": -0.672251403262238,
      "param2": -1.3660279641687287,
      "param3": 0.0446526305943824,
      "param4": 0.6047868572704881
    },
    {
      "type": "mod",
      "param1": 0.4082348546855985,
      "param2": 1.471899000349788,
      "param3": 0.6368538746334746,
      "param4": 0
    },
    {
      "type": "mixColors",
      "param1": 0.7284004322793538,
      "param2": 1.353784380361644,
      "param3": 1.416172568867002,
      "param4": 0
    },
    {
      "type": "multiply",
      "param1": 0.40631257442585733,
      "param2": 1.4224684035931758,
      "param3": 0.759875935976444,
      "param4": 0
    },
    {
      "type": "invertCircle",
      "param1": 0.9377038422451364,
      "param2": 0,
      "param3": 0,
      "param4": 0
    },
    {
      "type": "sin",
      "param1": -1.4629014749352436,
      "param2": -2.9740750475919326,
      "param3": 3.1323661028871816,
      "param4": 0
    },
    {
      "type": "mod",
      "param1": 1.7317745392693094,
      "param2": 1.0370606085892033,
      "param3": 1.1573907286892724,
      "param4": 0
    },
    {
      "type": "tileTriangle",
      "param1": 0.33811545172972246,
      "param2": 0.6773934574299263,
      "param3": 0.9329960046434227,
      "param4": 0
    },
    {
      "type": "tan",
      "param1": -2.337619732395138,
      "param2": -2.911925085269788,
      "param3": -0.052649449552845784,
      "param4": 0
    },
    {
      "type": "abs",
      "param1": 0.27466415953920187,
      "param2": 1.8332629005733159,
      "param3": -1.3821672139850483,
      "param4": 0
    }
  ]
}

I loaded it on 2 different tabs in the app.

the art looks 100% the same

but the ui looks different,

For example in the operation 17

In the first tab i have colorshift

in the second tab i see contrast

Some other operations are also i different

Each time i load export codes i get different outcomes everytime on the UI
Some Operation Types remain the same, while other change and vary, but they shouldn’t.

My desired result is that export code will always work the same when loaded and give the same visual output and the exact same state of the ui as when it was created.

Any help would largely help me to improve my project.

Also i promise that if you help me solve this or any other problems that are mentioned in the code description in * Known Issues: , I will add you to the credits of the program only if you wish of course and with the option to add your Open Processing Profile Link.

Use randomSeed() in setup():

And replace all Math.random() w/ just random(), so it uses the defined seed.

2 Likes

Thanks for the comment, i tried that and it didn’t work
not only that it didn’t work,
now when i press randomize multuple times it always gives the exact same results in the exact same order.

function setup() {
    pixelDensity(1); // Set initial pixel density
    
    randomSeed(42); // Initialize a seed, can be any constant number
    
    initOperations();  // Initialize shader operations             
    createUI();    // Create the UI            
    initCanvas();     // Initialize the canvas     
    initTargets(); // Initialize animation targets
    createExportImportModal(); // Create the export/import modal
}

before this, every time you opened the program you got a different visual output and each time you clicked randomize you got a random output.

i hope you understand why this breaks the concept of the code and the randomize button.

Do you have any idea why it didn’t work? when i export stuff the UI operators are still different on some operators.

Also for example the noise function in the fragment shader uses sin and dot products. These are deterministic but not tied to randomSeed(42) in the setup. This could result in unexpected variations.

I was wondering, did you intend for randomSeed() to control shader-level randomness as well,?

randomSeed() is used for when we want a code to always generate the same sequence of “random” numbers.

If you call randomSeed() w/o passing any value, you’d get a random, but consistent, sequence of values.

That is another programming language. Unless you could pass arguments from JS to your shader code.

1 Like

Lets focus on the fact that it didn’t fix the import \ export problem
leave the fact that it broke the way the randomization worked, i might be able to fix that later
you thought that will fix my problem with the import \ export problem, why?

can you see it didn’t work and do you have any idea why?

I ran the sketch and set the number of operations to 4. They were

  1. reflectX
  2. noise
  3. multiply
  4. add

Then I clicked on export and in the output the first 4 operations were

  1. reflectX
  2. reflectY
  3. invertCircle
  4. affine

The full results are shown below but if it is exporting in the wrong order then the import will not work properly either. I strongly suggest that you check your export settings code and get it to work as expected before worrying about importing them.

It might help if you include the operation number as well as type in the json export file as this might help highlight the problem e.g.

"operations": [
      {
        "id": 1,
        "type": "reflectX",
        "param1": -0.10977876093238592,
        "param2": 1.216162938857451,
        "param3": 0.32400561729446054,
        "param4": 0
      },
      {
      ...

Exported settings

{
    "numOperations": 4,
    "operationTypes": [
      1,
      15,
      8,
      7,
      10,
      22,
      23,
      24,
      23,
      14,
      15,
      15,
      15,
      6,
      7,
      10,
      23,
      9,
      24,
      15,
      6,
      11,
      7,
      7,
      6,
      23,
      6,
      23,
      6,
      6
    ],
    "params1": [
      -0.1,
      0.2,
      0.8,
      -0.3,
      0.8,
      1.3,
      0.9,
      0.6,
      0.8,
      -0.2,
      1.1,
      0.4,
      -0.2,
      0.1,
      1.4,
      0.3,
      1.1,
      1.1,
      0.6,
      0.1,
      1.1,
      0.3,
      1.2,
      0.6,
      0.3,
      1.1,
      1.5,
      1.2,
      -0.9,
      -0.7
    ],
    "params2": [
      1.2,
      0.3,
      0,
      1,
      0,
      0,
      0,
      0,
      0,
      0.6,
      1.2,
      -0.2,
      -0.8,
      0.9,
      1.2,
      1.9,
      0,
      0.8,
      0,
      -0.2,
      -0.2,
      -0.3,
      0.9,
      1.4,
      0.8,
      0,
      0.3,
      0,
      -1.5,
      0.2
    ],
    "params3": [
      0.3,
      1.4,
      0,
      0.9,
      0,
      0,
      0,
      0,
      0,
      -1.8,
      0.6,
      0.4,
      1,
      -1.2,
      1.2,
      1.2,
      0,
      1.6,
      0,
      0.9,
      -0.5,
      1.6,
      0.3,
      0.4,
      0.8,
      0,
      -0.7,
      0,
      -1.2,
      -0.9
    ],
    "params4": [
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0.91,
      0.67,
      0.91,
      0,
      0,
      0,
      0,
      0,
      0,
      0.81,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0
    ],
    "aaEnabled": true,
    "aaLevel": 2,
    "operations": [
      {
        "type": "reflectX",
        "param1": -0.10977876093238592,
        "param2": 1.216162938857451,
        "param3": 0.32400561729446054,
        "param4": 0
      },
      {
        "type": "reflectY",
        "param1": 0.1841117434669286,
        "param2": 0.30799820786342025,
        "param3": 1.4251477781217545,
        "param4": 0
      },
      {
        "type": "invertCircle",
        "param1": 0.8091240604873746,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "affine",
        "param1": -0.32273692823946476,
        "param2": 1.0227261234540492,
        "param3": 0.9088462698273361,
        "param4": 0
      },
      {
        "type": "invertCircle",
        "param1": 0.7566905767424031,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "saturation",
        "param1": 1.3,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "brightness",
        "param1": 0.9398123954888433,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "gamma",
        "param1": 0.6378391256555915,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "brightness",
        "param1": 0.8,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "tan",
        "param1": -0.23020814488755414,
        "param2": 0.6100335167674475,
        "param3": -1.7715518316503447,
        "param4": 0
      },
      {
        "type": "noise",
        "param1": 1.0901095087174326,
        "param2": 1.2382018030621111,
        "param3": 0.5644458790775388,
        "param4": 0.9141792431473732
      },
      {
        "type": "noise",
        "param1": 0.4364032067824155,
        "param2": -0.24402658129110932,
        "param3": 0.3629803347866982,
        "param4": 0.6749941245652735
      },
      {
        "type": "noise",
        "param1": -0.2206440099980682,
        "param2": -0.7625381159596145,
        "param3": 0.9507362411823124,
        "param4": 0.9083429845049977
      },
      {
        "type": "rotate",
        "param1": 0.08792248601093888,
        "param2": 0.8842312765773386,
        "param3": -1.2261511869728565,
        "param4": 0
      },
      {
        "type": "add",
        "param1": 1.420795989735052,
        "param2": 1.1753088993951677,
        "param3": 1.1762609037104994,
        "param4": 0
      },
      {
        "type": "mod",
        "param1": 0.3056428776821122,
        "param2": 1.8595029661431908,
        "param3": 1.2232486431719736,
        "param4": 0
      },
      {
        "type": "brightness",
        "param1": 1.0744384827557951,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "divide",
        "param1": 1.0649454413447528,
        "param2": 0.8412495368160307,
        "param3": 1.6157638655509798,
        "param4": 0
      },
      {
        "type": "gamma",
        "param1": 0.5844828385859728,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "noise",
        "param1": 0.12695099087432027,
        "param2": -0.19371100352145731,
        "param3": 0.9000673647969961,
        "param4": 0.8064321064157411
      },
      {
        "type": "affine",
        "param1": 1.1291540383826941,
        "param2": -0.1660571275278926,
        "param3": -0.5319944468792528,
        "param4": 0
      },
      {
        "type": "abs",
        "param1": 0.2534385174512863,
        "param2": -0.3074675062671304,
        "param3": 1.5934025961905718,
        "param4": 0
      },
      {
        "type": "add",
        "param1": 1.1604619828984142,
        "param2": 0.9125791447702796,
        "param3": 0.331443911511451,
        "param4": 0
      },
      {
        "type": "add",
        "param1": 0.6215557444840669,
        "param2": 1.4060825035441666,
        "param3": 0.4097070160321891,
        "param4": 0
      },
      {
        "type": "affine",
        "param1": 0.34732256177812815,
        "param2": 0.7953476572874933,
        "param3": 0.7674503833986819,
        "param4": 0
      },
      {
        "type": "brightness",
        "param1": 1.1243185920175165,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "reflectY",
        "param1": 1.490538491634652,
        "param2": 0.29099208768457174,
        "param3": -0.6870429196860641,
        "param4": 0
      },
      {
        "type": "brightness",
        "param1": 1.2,
        "param2": 0,
        "param3": 0,
        "param4": 0
      },
      {
        "type": "reflectY",
        "param1": -0.8836624142713845,
        "param2": -1.471911157714203,
        "param3": -1.2115903152152896,
        "param4": 0
      },
      {
        "type": "rotate",
        "param1": -0.7192379313055426,
        "param2": 0.1905975602567196,
        "param3": -0.8878097652923316,
        "param4": 0
      }
    ]
  }
3 Likes

the thing is that the visual output is exactly the same which is the most important thing, only some operations in the ui are i different, any ideas why?

OK So I tried these actions

  1. modified my 4 operations to get an image I could remember
  2. saved the export settings.
  3. stopped the sketch
  4. restarted the sketch
  5. imported my saved settings

I got the same image as before but the UI control types did not match the original settings.

As to some of the settings being the same that could simply be coincidence but the most likely thing is that they are not being exported in the order that they appear, and they are not being displayed in the order they are in the file. That is why I suggested including the actual op number in the json file so you can see the order the ops are being stored.

3 Likes

I tried and your advice helped moving towards the fix but the full fix was a bit more complex…

Let me explain:

The original code did not have a clear way to store the exact state of the operations for export/import.
The corrected code introduced an export and import functionality that serializes the application state into a JSON string (which is just text):

function generateExportCode() {
    const exportData = {
        numOperations: targetNumOperations,
        operationTypes: targetOperationTypes,
        params1: targetParams1,
        params2: targetParams2,
        params3: targetParams3,
        params4: targetParams4,
        aaEnabled: aaEnabled,
        aaLevel: aaLevel,
        operations: operations.map(op => ({
            type: op.type,
            param1: op.param1,
            param2: op.param2,
            param3: op.param3,
            param4: op.param4
        }))

    };
    const jsonString = JSON.stringify(exportData, null, 2);
    exportTextArea.value(jsonString);
}

function importSettings() {
  try {
        const jsonString = importTextArea.value();
        const importData = JSON.parse(jsonString);

        if (importData) {
            // Directly use imported data
            targetNumOperations = importData.numOperations;
            targetOperationTypes = importData.operationTypes.slice();
            targetParams1 = importData.params1.slice();
            targetParams2 = importData.params2.slice();
            targetParams3 = importData.params3.slice();
            targetParams4 = importData.params4.slice();

            aaEnabled = importData.aaEnabled;
            aaLevel = importData.aaLevel;

            operations = importData.operations.map(op => ({
                type: op.type,
                param1: op.param1,
                param2: op.param2,
                param3: op.param3,
                param4: op.param4
            }));

            aaCheckbox.checked(aaEnabled);
            if (aaEnabled) {
                aaLevelDropdown.removeAttribute('disabled');
                aaLevelDropdown.selected(`${aaLevel}x`);
            } else {
                aaLevelDropdown.attribute('disabled', '');
            }

            numOperationsSlider.value(targetNumOperations);

            for (let i = 0; i < numOperationsMax; i++) {
                let opName = operations[i].type;
                operationTypeDropdowns[i].selected(opName);
                param1Sliders[i].value(operations[i].param1);
                param2Sliders[i].value(operations[i].param2);
                param3Sliders[i].value(operations[i].param3);
                param4Sliders[i].value(operations[i].param4);
                updateParamVisibility(i);
            }

            closeExportImportModal();
            applyImportedSettings();
        }
    } catch (e) {
        console.error("Error parsing import data:", e);
        alert('Invalid import data');
    }
}

This code captures the exact settings (number of operations, operation types, their parameters, etc.) at the moment of export. Upon import, this JSON string is parsed back into Javascript objects and used to set UI, thus keeping a synchronized output.
This part is esential since, before, the only thing that was being saved and loaded was the current visuals of the image, that is not enough since i want the ui to reflect what was saved too,This ensures that all relevant data of the visual and the ui is stored and can be loaded as needed.

The original code used getOperationIndex to get the index of each operation, in theory it’s fine, but in practice that was not good enough to keep a consistent output.

function getOperationIndex(index, typeName) {
   if (index == 0) {
       return baseOperationNames.indexOf(typeName);
   } else {
       return effectOperationNames.indexOf(typeName) + baseOperationNames.length;
   }
}

The corrected code ensures the operation types were never changed during the animation, as the parameters of each operation is what’s changing and interpolating:

if (isAnimating) {
    let elapsed = millis() - animationStartTime; // Calculate the elapsed time since the animation started
    let t = elapsed / animationDuration; // Calculate the normalized time (0-1) for the animation
    t = constrain(t, 0, 1); // Ensure the normalized time is within the range of 0 and 1

    // Apply easing to the animation progress (easeInOutCubic)
     if (t < 0.5) {
        t = 4.0 * t * t * t;
    } else {
        t = 1.0 - pow(-2.0 * t + 2.0, 3.0) / 2.0;
    }

    // Interpolate number of operations based on the normalized time
    currentNumOperations = lerp(currentNumOperations, targetNumOperations, t);

    // Interpolate parameters of each operation based on the normalized time
    for (let i = 0; i < numOperationsMax; i++) {
        currentParams1[i] = lerp(currentParams1[i], targetParams1[i], t);
        currentParams2[i] = lerp(currentParams2[i], targetParams2[i], t);
        currentParams3[i] = lerp(currentParams3[i], targetParams3[i], t);
        currentParams4[i] = lerp(currentParams4[i], targetParams4[i], t);
        currentOperationTypes[i] = targetOperationTypes[i]; //Update the operation type to the target one
    }
   }

This makes it so that if the operation type is changed or saved, it will never be reevaluated to a different type during animation.This guarantees that a “colorShift” operation will never change to a “contrast” operation during an animation, maintaining ui and visual consistency…

The original code’s random operation generation was very naive, simply assigning random operations and parameters without consideration of visual result…

The new code has a significantly smarter initOperations function, it includes some key improvements… but i won’t elaborate on that more here although it was related to part of the fix.

1 Like