JSON.stringify them on one end, and then
JSON.parse them back on the other end.
this is very cool, but it's limited: it can't support cycles, can't deduplicate shared data, and can't send code — those issues are actually related: part of the problem with serializing code is that functions are often recursive or even mutually recursive (which takes either cyclical representations to send, or otherwise a layer of indirection to remove cyclicality) and that many functions point back to other shared functions in a structure that looks like a DAG, where you would ideally want the upstream nodes to be deduplicated.
psi tries to address these issues by being a universal format that supports cycles and efficiently unifies shared structures, such that they remain a single big piece of data pointed to for example using an IPFS address, and only small changes can be sent along.
in addition, psi's recommended usage of randomly-generated identifiers to represent concepts also makes psi payloads universal as opposed to only contextually meaningful, which hopefully makes things nice for things like interoperation or debugging.