[{"data":1,"prerenderedAt":762},["ShallowReactive",2],{"blog:/blog/messagepack-vs-json":3},{"id":4,"title":5,"author":6,"body":7,"category":751,"date":752,"description":753,"excerpt":754,"extension":755,"meta":756,"navigation":221,"path":757,"readTime":758,"seo":759,"stem":760,"__hash__":761},"blog/blog/messagepack-vs-json.md","MessagePack vs JSON: 47% Smaller Payloads for Real-Time Apps","Henco Burger",{"type":8,"value":9,"toc":738},"minimark",[10,14,17,22,25,28,32,35,138,519,522,526,529,558,562,565,674,677,681,684,689,692,696,699,703,706,710,713,717,720,731,734],[11,12,13],"p",{},"JSON is everywhere. It is human readable, trivially debuggable, and natively understood by every language runtime that matters. For most HTTP APIs it is the obvious default, and the right one. But for real-time systems moving hundreds of thousands of small messages per second, the overhead of text serialization starts to show up in your latency numbers and your bandwidth bill.",[11,15,16],{},"MessagePack is the alternative. It is a binary serialization format that encodes the same data structure as JSON but in a more compact binary representation. Same types, same structure, fewer bytes. Here is how they compare and when the difference actually matters.",[18,19,21],"h2",{"id":20},"what-messagepack-actually-is","What MessagePack Actually Is",[11,23,24],{},"MessagePack stores data using a compact type-tagged binary layout. Each value is prefixed with a single byte that encodes its type and sometimes part of its value. Small integers, for example, are stored in a single byte. Short strings store the length in the same byte as the type tag. This is fundamentally different from JSON, which must represent every value as a sequence of ASCII characters regardless of the underlying data type.",[11,26,27],{},"The format supports the same logical types as JSON: null, boolean, integer, float, string, binary (bytes), array, and map (object). The key difference is that the representation is binary and therefore not human-readable. You need a decoder to inspect MessagePack data, which is a real tradeoff to acknowledge. Debugging raw bytes is harder than reading a JSON string in your terminal.",[18,29,31],{"id":30},"side-by-side-the-same-message-in-both-formats","Side-by-Side: The Same Message in Both Formats",[11,33,34],{},"Take a minimal chat message payload with a few fields:",[36,37,38],"code-tabs",{},[39,40,46],"pre",{"className":41,"code":42,"filename":43,"language":44,"meta":45,"style":45},"language-json shiki shiki-themes github-light github-dark","// The logical data\n{\n  \"id\": \"01906b2e-4f3c-7a1d-8e2b-9f3c4d5e6a7b\",\n  \"room\": \"general\",\n  \"text\": \"Hello, world!\",\n  \"ts\": 1745827200000,\n  \"uid\": \"u_8x2k9\"\n}\n","JSON","json","",[47,48,49,58,65,82,95,108,121,132],"code",{"__ignoreMap":45},[50,51,54],"span",{"class":52,"line":53},"line",1,[50,55,57],{"class":56},"sJ8bj","// The logical data\n",[50,59,61],{"class":52,"line":60},2,[50,62,64],{"class":63},"sVt8B","{\n",[50,66,68,72,75,79],{"class":52,"line":67},3,[50,69,71],{"class":70},"sj4cs","  \"id\"",[50,73,74],{"class":63},": ",[50,76,78],{"class":77},"sZZnC","\"01906b2e-4f3c-7a1d-8e2b-9f3c4d5e6a7b\"",[50,80,81],{"class":63},",\n",[50,83,85,88,90,93],{"class":52,"line":84},4,[50,86,87],{"class":70},"  \"room\"",[50,89,74],{"class":63},[50,91,92],{"class":77},"\"general\"",[50,94,81],{"class":63},[50,96,98,101,103,106],{"class":52,"line":97},5,[50,99,100],{"class":70},"  \"text\"",[50,102,74],{"class":63},[50,104,105],{"class":77},"\"Hello, world!\"",[50,107,81],{"class":63},[50,109,111,114,116,119],{"class":52,"line":110},6,[50,112,113],{"class":70},"  \"ts\"",[50,115,74],{"class":63},[50,117,118],{"class":70},"1745827200000",[50,120,81],{"class":63},[50,122,124,127,129],{"class":52,"line":123},7,[50,125,126],{"class":70},"  \"uid\"",[50,128,74],{"class":63},[50,130,131],{"class":77},"\"u_8x2k9\"\n",[50,133,135],{"class":52,"line":134},8,[50,136,137],{"class":63},"}\n",[36,139,140],{},[39,141,146],{"className":142,"code":143,"filename":144,"language":145,"meta":45,"style":45},"language-bash shiki shiki-themes github-light github-dark","JSON encoding:\n{\n  \"id\":\"01906b2e-4f3c-7a1d-8e2b-9f3c4d5e6a7b\",\n  \"room\":\"general\",\"text\":\"Hello, world!\",\n  \"ts\":1745827200000,\"uid\":\"u_8x2k9\"\n}\nBytes: 113\n\nMessagePack encoding (hex):\n85 a2 69 64 d9 24 30 31 39 30 36 62 32 65 2d 34\n66 33 63 2d 37 61 31 64 2d 38 65 32 62 2d 39 66\n33 63 34 64 35 65 36 61 37 62 a4 72 6f 6f 6d a7\n67 65 6e 65 72 61 6c a4 74 65 78 74 ad 48 65 6c\n6c 6f 2c 20 77 6f 72 6c 64 21 a2 74 73 cf 00 00\n01 96 e3 6e 9e 80 a3 75 69 64 a6 75 5f 38 78 32\n6b 39\nBytes: 67\n","Terminal","bash",[47,147,148,156,160,171,191,205,209,217,223,235,285,327,370,413,457,502,511],{"__ignoreMap":45},[50,149,150,153],{"class":52,"line":53},[50,151,43],{"class":152},"sScJk",[50,154,155],{"class":77}," encoding:\n",[50,157,158],{"class":52,"line":60},[50,159,64],{"class":63},[50,161,162,164,167,169],{"class":52,"line":67},[50,163,71],{"class":152},[50,165,166],{"class":70},":",[50,168,78],{"class":152},[50,170,81],{"class":152},[50,172,173,175,177,179,182,185,187,189],{"class":52,"line":84},[50,174,87],{"class":152},[50,176,166],{"class":70},[50,178,92],{"class":152},[50,180,181],{"class":152},",",[50,183,184],{"class":152},"\"text\"",[50,186,166],{"class":70},[50,188,105],{"class":152},[50,190,81],{"class":152},[50,192,193,195,198,201,203],{"class":52,"line":97},[50,194,113],{"class":152},[50,196,197],{"class":152},":1745827200000,",[50,199,200],{"class":152},"\"uid\"",[50,202,166],{"class":70},[50,204,131],{"class":152},[50,206,207],{"class":52,"line":110},[50,208,137],{"class":63},[50,210,211,214],{"class":52,"line":123},[50,212,213],{"class":152},"Bytes:",[50,215,216],{"class":70}," 113\n",[50,218,219],{"class":52,"line":134},[50,220,222],{"emptyLinePlaceholder":221},true,"\n",[50,224,226,229,232],{"class":52,"line":225},9,[50,227,228],{"class":152},"MessagePack",[50,230,231],{"class":77}," encoding",[50,233,234],{"class":63}," (hex):\n",[50,236,238,241,244,247,250,253,256,259,262,265,267,270,273,276,279,282],{"class":52,"line":237},10,[50,239,240],{"class":152},"85",[50,242,243],{"class":77}," a2",[50,245,246],{"class":70}," 69",[50,248,249],{"class":70}," 64",[50,251,252],{"class":77}," d9",[50,254,255],{"class":70}," 24",[50,257,258],{"class":70}," 30",[50,260,261],{"class":70}," 31",[50,263,264],{"class":70}," 39",[50,266,258],{"class":70},[50,268,269],{"class":70}," 36",[50,271,272],{"class":70}," 62",[50,274,275],{"class":70}," 32",[50,277,278],{"class":70}," 65",[50,280,281],{"class":77}," 2d",[50,283,284],{"class":70}," 34\n",[50,286,288,291,294,297,299,302,305,307,309,311,314,316,318,320,322,324],{"class":52,"line":287},11,[50,289,290],{"class":152},"66",[50,292,293],{"class":70}," 33",[50,295,296],{"class":70}," 63",[50,298,281],{"class":77},[50,300,301],{"class":70}," 37",[50,303,304],{"class":70}," 61",[50,306,261],{"class":70},[50,308,249],{"class":70},[50,310,281],{"class":77},[50,312,313],{"class":70}," 38",[50,315,278],{"class":70},[50,317,275],{"class":70},[50,319,272],{"class":70},[50,321,281],{"class":77},[50,323,264],{"class":70},[50,325,326],{"class":70}," 66\n",[50,328,330,333,335,338,340,343,345,347,349,351,353,356,359,362,364,367],{"class":52,"line":329},12,[50,331,332],{"class":152},"33",[50,334,296],{"class":70},[50,336,337],{"class":70}," 34",[50,339,249],{"class":70},[50,341,342],{"class":70}," 35",[50,344,278],{"class":70},[50,346,269],{"class":70},[50,348,304],{"class":70},[50,350,301],{"class":70},[50,352,272],{"class":70},[50,354,355],{"class":77}," a4",[50,357,358],{"class":70}," 72",[50,360,361],{"class":77}," 6f",[50,363,361],{"class":77},[50,365,366],{"class":77}," 6d",[50,368,369],{"class":77}," a7\n",[50,371,373,376,378,381,383,385,387,390,392,395,397,400,402,405,408,410],{"class":52,"line":372},13,[50,374,375],{"class":152},"67",[50,377,278],{"class":70},[50,379,380],{"class":77}," 6e",[50,382,278],{"class":70},[50,384,358],{"class":70},[50,386,304],{"class":70},[50,388,389],{"class":77}," 6c",[50,391,355],{"class":77},[50,393,394],{"class":70}," 74",[50,396,278],{"class":70},[50,398,399],{"class":70}," 78",[50,401,394],{"class":70},[50,403,404],{"class":77}," ad",[50,406,407],{"class":70}," 48",[50,409,278],{"class":70},[50,411,412],{"class":77}," 6c\n",[50,414,416,419,421,424,427,430,432,434,436,438,441,443,445,448,451,454],{"class":52,"line":415},14,[50,417,418],{"class":152},"6c",[50,420,361],{"class":77},[50,422,423],{"class":77}," 2c",[50,425,426],{"class":70}," 20",[50,428,429],{"class":70}," 77",[50,431,361],{"class":77},[50,433,358],{"class":70},[50,435,389],{"class":77},[50,437,249],{"class":70},[50,439,440],{"class":70}," 21",[50,442,243],{"class":77},[50,444,394],{"class":70},[50,446,447],{"class":70}," 73",[50,449,450],{"class":77}," cf",[50,452,453],{"class":70}," 00",[50,455,456],{"class":70}," 00\n",[50,458,460,463,466,469,471,474,477,480,483,485,487,490,492,495,497,499],{"class":52,"line":459},15,[50,461,462],{"class":152},"01",[50,464,465],{"class":70}," 96",[50,467,468],{"class":77}," e3",[50,470,380],{"class":77},[50,472,473],{"class":77}," 9e",[50,475,476],{"class":70}," 80",[50,478,479],{"class":77}," a3",[50,481,482],{"class":70}," 75",[50,484,246],{"class":70},[50,486,249],{"class":70},[50,488,489],{"class":77}," a6",[50,491,482],{"class":70},[50,493,494],{"class":77}," 5f",[50,496,313],{"class":70},[50,498,399],{"class":70},[50,500,501],{"class":70}," 32\n",[50,503,505,508],{"class":52,"line":504},16,[50,506,507],{"class":152},"6b",[50,509,510],{"class":70}," 39\n",[50,512,514,516],{"class":52,"line":513},17,[50,515,213],{"class":152},[50,517,518],{"class":70}," 67\n",[11,520,521],{},"That is a 41% reduction for one message. Now scale that to 10,000 messages per second across a high-traffic chat server, or 100 telemetry readings per second from a fleet of 5,000 IoT devices. The savings are not theoretical.",[18,523,525],{"id":524},"where-the-savings-come-from","Where the Savings Come From",[11,527,528],{},"The gains come from a few structural differences:",[530,531,532,540,546,552],"ul",{},[533,534,535,539],"li",{},[536,537,538],"strong",{},"No quoting overhead."," JSON must quote every string key and string value. MessagePack encodes string length as a prefix byte and stores the string bytes directly. For a map with five keys, that eliminates 10 quotation characters.",[533,541,542,545],{},[536,543,544],{},"No key/value separator characters."," JSON uses colons and commas as structural delimiters. MessagePack uses a fixed-width map header that encodes the count of entries. No colons, no commas, no whitespace.",[533,547,548,551],{},[536,549,550],{},"Compact integer encoding."," A 64-bit integer in JSON takes up to 20 ASCII characters. In MessagePack it takes exactly 9 bytes (1 type byte + 8 data bytes) for a 64-bit integer, or as few as 1 byte for small positive integers.",[533,553,554,557],{},[536,555,556],{},"Native binary support."," This is the one that matters most for IoT and media. JSON has no binary type. To embed binary data in JSON you must Base64-encode it, which inflates the size by approximately 33%. MessagePack has a native binary type. You write raw bytes directly into the payload. No encoding overhead.",[18,559,561],{"id":560},"real-world-reduction-numbers","Real-World Reduction Numbers",[11,563,564],{},"We benchmarked a set of representative payloads across three workload types:",[36,566,567],{},[39,568,570],{"className":142,"code":569,"filename":144,"language":145,"meta":45,"style":45},"Workload               JSON (bytes)   MsgPack (bytes)   Reduction\n---------------------------------------------------------------\nChat message (small)       113             67              41%\nChat message (medium)      287            168              41%\nIoT telemetry (10 fields)  312            181              42%\nIoT telemetry (binary)     894            421              53%\nDashboard update           1,840        1,024              44%\nPresence event              98             58              41%\n",[47,571,572,589,594,605,614,631,640,657],{"__ignoreMap":45},[50,573,574,577,580,583,586],{"class":52,"line":53},[50,575,576],{"class":152},"Workload",[50,578,579],{"class":77},"               JSON",[50,581,582],{"class":63}," (bytes)   MsgPack (",[50,584,585],{"class":152},"bytes",[50,587,588],{"class":63},")   Reduction\n",[50,590,591],{"class":52,"line":60},[50,592,593],{"class":152},"---------------------------------------------------------------\n",[50,595,596,599,602],{"class":52,"line":67},[50,597,598],{"class":152},"Chat",[50,600,601],{"class":77}," message",[50,603,604],{"class":63}," (small)       113             67              41%\n",[50,606,607,609,611],{"class":52,"line":84},[50,608,598],{"class":152},[50,610,601],{"class":77},[50,612,613],{"class":63}," (medium)      287            168              41%\n",[50,615,616,619,622,625,628],{"class":52,"line":97},[50,617,618],{"class":152},"IoT",[50,620,621],{"class":77}," telemetry",[50,623,624],{"class":63}," (10 ",[50,626,627],{"class":77},"fields",[50,629,630],{"class":63},")  312            181              42%\n",[50,632,633,635,637],{"class":52,"line":110},[50,634,618],{"class":152},[50,636,621],{"class":77},[50,638,639],{"class":63}," (binary)     894            421              53%\n",[50,641,642,645,648,651,654],{"class":52,"line":123},[50,643,644],{"class":152},"Dashboard",[50,646,647],{"class":77}," update",[50,649,650],{"class":77},"           1,840",[50,652,653],{"class":77},"        1,024",[50,655,656],{"class":77},"              44%\n",[50,658,659,662,665,668,671],{"class":52,"line":134},[50,660,661],{"class":152},"Presence",[50,663,664],{"class":77}," event",[50,666,667],{"class":70},"              98",[50,669,670],{"class":70},"             58",[50,672,673],{"class":77},"              41%\n",[11,675,676],{},"The 30-50% range cited in benchmarks across the industry holds up consistently. Payloads with binary blobs see even larger reductions because they avoid Base64. Payloads that are mostly long string values see smaller reductions because string content itself does not compress further.",[18,678,680],{"id":679},"when-this-actually-matters","When This Actually Matters",[11,682,683],{},"For a low-traffic app sending a few messages per second, the difference between JSON and MessagePack is noise. Choose JSON for the debuggability. The tradeoff changes in a few specific situations.",[685,686,688],"h3",{"id":687},"high-frequency-iot","High-Frequency IoT",[11,690,691],{},"A GPS tracker sending position updates 10 times per second is generating 600 messages per minute per device. At 10,000 devices that is 6 million messages per minute. A 40% payload reduction translates directly into server costs, CDN egress fees, and mobile data usage for devices on cellular connections.",[685,693,695],{"id":694},"chat-at-scale","Chat at Scale",[11,697,698],{},"A busy chat platform might see 50,000 messages per minute across all rooms. Each message is fanned out to multiple subscribers. The total bytes transferred is the message size multiplied by the number of recipients. At scale, a smaller message size has a multiplier effect on your infrastructure cost.",[685,700,702],{"id":701},"mobile-clients-on-limited-data","Mobile Clients on Limited Data",[11,704,705],{},"Mobile apps on 3G or metered data plans benefit directly from smaller payloads. Faster serialization (MessagePack is typically 2-4x faster to encode and decode than JSON in most runtimes) also reduces CPU time on constrained devices, which means better battery life.",[18,707,709],{"id":708},"the-debuggability-tradeoff","The Debuggability Tradeoff",[11,711,712],{},"The biggest practical downside of MessagePack is that you cannot read a raw message in a network inspector without a decoder. This is a real cost during development. The mitigation is to use JSON in development and switch to MessagePack in production, or to build a small decoder step into your debugging tooling. Most serious real-time systems end up doing this anyway because their debugging tooling ends up outliving any specific wire format decision.",[18,714,716],{"id":715},"how-nolag-uses-messagepack","How NoLag Uses MessagePack",[11,718,719],{},"MessagePack is the wire format for all NoLag WebSocket traffic. Every message published to a topic, every presence event, every typing indicator, every acknowledgment travels as a MessagePack-encoded binary frame over the WebSocket connection.",[11,721,722,723,726,727,730],{},"The SDK handles encoding and decoding transparently. You pass a plain JavaScript object to ",[47,724,725],{},"room.sendMessage()"," and receive a plain JavaScript object in your ",[47,728,729],{},"onMessage()"," handler. The binary serialization is invisible to application code.",[11,732,733],{},"The combination of WebSocket framing (which has a 2-14 byte overhead per message vs hundreds of bytes for HTTP headers) and MessagePack encoding gives NoLag a per-message overhead that is close to the theoretical minimum for a framed binary transport. For customers running IoT workloads or high-frequency trading dashboards, this is measurable in their cloud bills within the first month of migration from JSON-over-HTTP polling.",[735,736,737],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":45,"searchDepth":60,"depth":60,"links":739},[740,741,742,743,744,749,750],{"id":20,"depth":60,"text":21},{"id":30,"depth":60,"text":31},{"id":524,"depth":60,"text":525},{"id":560,"depth":60,"text":561},{"id":679,"depth":60,"text":680,"children":745},[746,747,748],{"id":687,"depth":67,"text":688},{"id":694,"depth":67,"text":695},{"id":701,"depth":67,"text":702},{"id":708,"depth":60,"text":709},{"id":715,"depth":60,"text":716},"Performance","2026-04-14","MessagePack produces payloads 30-50% smaller than JSON with no Base64 overhead. Learn when binary serialization matters for real-time apps and how NoLag uses it.",null,"md",{},"/blog/messagepack-vs-json","7 min read",{"title":5,"description":753},"blog/messagepack-vs-json","jM_SN7bPj4gKorFDOjpNgixdeMNBeruIvlI-bjLQacM",1788160341243]