What technology to use for saving
When your game is single-player, you can save to the browser's localstorage. But keep in mind that the specification guarantees only 5 MB of storage per website (some browsers will give you more, but you can't rely on that). It also allows web browsers to delete old localstorage data without a warning at their discretion.
An alternative is to use the JavaScript file API to create or read savegame files which the user stores on their filesystem. This appears to the user like a web download / web upload (even though the data never leaves their device). This is less convenient for the player than localstorage, because both saving and loading require additional user interaction. That means no autosave and no one-button quicksave. But it makes it easier for players to transfer savegames between devices and frees you from the space restrictions of localstorage.
When you switch to an online game with a Node.js backend, you should consider saving to a database. If you don't want to use a database for some reason (you do want one, trust me), server-sided files are an alternative option.
What data to save
Dirty solution: Just serialize and store the whole chunk, including the procedurally generated data. This is what Minecraft does, for example. One advantage is that it is easy to do. Another is that it is good for backward compatibility. If you change your world generation algorithms, those chunks generated by the old version are unchanged. The problem is that this can amount to quite a lot of data, especially when your world is very large and/or complex.
Elegant solution: Only store the following:
- the seed which was used to generate the Perlin-noise for the chunk (note that the JavaScript standard RNG doesn't allow seed values, so you need to implement an own RNG. Don't be afraid, there are many well-documented PRNG algorithms around which aren't hard to implement)
- any changes the player made.
In order to do the latter, you need to keep a list of changes to each chunk. If the player planted a tree at 317:56, push an object { x:317, y:56, object: "Tree" } to the changes-array. When they chopped down a tree at coordinates 456:194, push an object { x:456, y:194, object: null }. If they chopped the tree at 317:56 down again (restoring the original state), remove the object with x:317, y:56.
The advantage of this method is that you minimize the amount of data you need to store when the player usually doesn't make that many changes to the game world. But keep in mind that if your player has a lot of influence on the game-world, this method can amount to even more data than just storing the whole chunk.
How to serialize data
JSON.stringify and JSON.parse are your friends here. They can turn a JavaScript object (or array) into a string and back. But keep in mind that it can not serialize functions! Any fields which contain functions will be omitted.
If the JSON code turns out to be too large for your use-case, consider running a stock compression algorithm like ZIP on it.