Optimize QR code
Closed this issue · 9 comments
by changing to uppercase a QR code only needs 5.5 bits instead of 8 bits
bech32 is designed to work in uppercase QR
the proposed strings are not 100% in the supported alphabet, but QR code has a mixed mode that means it can switch mode inside the same QR code
i suggest we simply uppercase the lightning invoice to start with, all wallets i know support uppercase lightning invoices.
bc1 on chain addresses should also be possible to uppercase in the same way since they are bech32
some wallets will probably have issues, but it is them that have to fix it.
small example, the example on https://bitcoinqr.dev/
only change is lightning invoice uppercased:
it is quite obvious that it needs fewer bits
some wallets will probably have issues, but it is them that have to fix it.
@robtex Yeah, I'm not too worried about wallet support for uppercasing. I think there's fairly wide support for it, actually. This data is from December 2020 but already has promising results. Wouldn't surprise me if some of these red Xs have become checkmarks in the past year and a half. btcpayserver/btcpayserver#2110
Ran some tests by generating QR codes with this tool. I decided to run this test with different variations, like changing number of URI params and changing the invoice.
In every case, the uppercase QR is a lower-resolution image. These codes are being generated with the qrcode NPM library. This lib is supposed to automatically choose the correct QR code mode for you based on what's most efficient for the input string. I imagine it's selecting "mixed" for most of these, but regardless, it's supposed to be choosing the most size efficient mode.
So I think this can serve as a proof of concept that the uppercasing can work very well.
URI with label and message
Build command
npm run qr -- --amt=0.00001000 --name='sbddesign' --description='For lunch Tuesday' --btc='bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u' --ln='lnbc10u1p3pj257pp5yztkwjcz5ftl5laxkav23zmzekaw37zk6kmv80pk4xaev5qhtz7qdpdwd3xger9wd5kwm36yprx7u3qd36kucmgyp282etnv3shjcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqjcewm5cjwz4a6rfjx77c490yced6pemk0upkxhy89cmm7sct66k8gneanwykzgdrwrfje69h9u5u0w57rrcsysas7gadwmzxc8c6t0spjazup6' --file qr-bip21.png
URI
bitcoin:bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u?amount=0.00001&lightning=lnbc10u1p3pj257pp5yztkwjcz5ftl5laxkav23zmzekaw37zk6kmv80pk4xaev5qhtz7qdpdwd3xger9wd5kwm36yprx7u3qd36kucmgyp282etnv3shjcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqjcewm5cjwz4a6rfjx77c490yced6pemk0upkxhy89cmm7sct66k8gneanwykzgdrwrfje69h9u5u0w57rrcsysas7gadwmzxc8c6t0spjazup6&label=sbddesign&message=For%20lunch%20Tuesday
URI with label and message (uppercase)
Build command
npm run qr -- --amt=0.00001000 --name='sbddesign' --description='For lunch Tuesday' --btc='BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U' --ln='LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6' --file qr_bip21_uppercase.png
URI
bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?amount=0.00001&label=sbddesign&message=For%20lunch%20Tuesday&lightning=LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6
URI with label and message - alternate invoice
Build command
npm run qr -- --amt=0.00001000 --name='sbddesign' --description='For lunch Tuesday' --btc='bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u' --ln='lnbc10u1p3ff0wxpp5p3vchn8ehvpk2drll24x222crhx4auqj2aduxpuaxw9gj2jfprxsd8qfa3xummcd9hh2umv0ysxcmmwvusx6etddusz6gzzd96xxmmfdcsxjurnw4kjqer0d3hhygrnd96zqctdv46zugzyda6kymr994ehqetwvss8qun0vfkx2mfqwa5xjar9wpshqetjypf5ssfdxg6nvtpqv3hh2cnvv5khxur9dejzqurjda3xcetdypxk2untd3jjq4rjv4jjqmtfdejhygrzd3hkx6eqcqzpgxqzjcsp5ljee7p2gufjtda6jj5d3cxh977ew3npjx5ajhav68k3hf5xhd48q9qyyssqvjcg29x5jnas9lc5tmkstamgf8kghjqk2l0v326qgzq3s6xqftu9nmzkjt7t7607da9n44n0a8svtfuya0s4rtsr94tmtf7k8ng6tlsq2qdh2k' --file qr-bip21_longer.png
URI
bitcoin:bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u?amount=0.00001&label=sbddesign&message=For%20lunch%20Tuesday&lightning=lnbc10u1p3ff0wxpp5p3vchn8ehvpk2drll24x222crhx4auqj2aduxpuaxw9gj2jfprxsd8qfa3xummcd9hh2umv0ysxcmmwvusx6etddusz6gzzd96xxmmfdcsxjurnw4kjqer0d3hhygrnd96zqctdv46zugzyda6kymr994ehqetwvss8qun0vfkx2mfqwa5xjar9wpshqetjypf5ssfdxg6nvtpqv3hh2cnvv5khxur9dejzqurjda3xcetdypxk2untd3jjq4rjv4jjqmtfdejhygrzd3hkx6eqcqzpgxqzjcsp5ljee7p2gufjtda6jj5d3cxh977ew3npjx5ajhav68k3hf5xhd48q9qyyssqvjcg29x5jnas9lc5tmkstamgf8kghjqk2l0v326qgzq3s6xqftu9nmzkjt7t7607da9n44n0a8svtfuya0s4rtsr94tmtf7k8ng6tlsq2qdh2k
URI with label and message - alternate invoice (uppercase)
Build command
npm run qr -- --amt=0.00001000 --name='sbddesign' --description='For lunch Tuesday' --btc='BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U' --ln='LNBC10U1P3FF0WXPP5P3VCHN8EHVPK2DRLL24X222CRHX4AUQJ2ADUXPUAXW9GJ2JFPRXSD8QFA3XUMMCD9HH2UMV0YSXCMMWVUSX6ETDDUSZ6GZZD96XXMMFDCSXJURNW4KJQER0D3HHYGRND96ZQCTDV46ZUGZYDA6KYMR994EHQETWVSS8QUN0VFKX2MFQWA5XJAR9WPSHQETJYPF5SSFDXG6NVTPQV3HH2CNVV5KHXUR9DEJZQURJDA3XCETDYPXK2UNTD3JJQ4RJV4JJQMTFDEJHYGRZD3HKX6EQCQZPGXQZJCSP5LJEE7P2GUFJTDA6JJ5D3CXH977EW3NPJX5AJHAV68K3HF5XHD48Q9QYYSSQVJCG29X5JNAS9LC5TMKSTAMGF8KGHJQK2L0V326QGZQ3S6XQFTU9NMZKJT7T7607DA9N44N0A8SVTFUYA0S4RTSR94TMTF7K8NG6TLSQ2QDH2K' --file qr_bip21-longer_uppercase.png
URI
bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?amount=0.00001&label=sbddesign&message=For%20lunch%20Tuesday&lightning=LNBC10U1P3FF0WXPP5P3VCHN8EHVPK2DRLL24X222CRHX4AUQJ2ADUXPUAXW9GJ2JFPRXSD8QFA3XUMMCD9HH2UMV0YSXCMMWVUSX6ETDDUSZ6GZZD96XXMMFDCSXJURNW4KJQER0D3HHYGRND96ZQCTDV46ZUGZYDA6KYMR994EHQETWVSS8QUN0VFKX2MFQWA5XJAR9WPSHQETJYPF5SSFDXG6NVTPQV3HH2CNVV5KHXUR9DEJZQURJDA3XCETDYPXK2UNTD3JJQ4RJV4JJQMTFDEJHYGRZD3HKX6EQCQZPGXQZJCSP5LJEE7P2GUFJTDA6JJ5D3CXH977EW3NPJX5AJHAV68K3HF5XHD48Q9QYYSSQVJCG29X5JNAS9LC5TMKSTAMGF8KGHJQK2L0V326QGZQ3S6XQFTU9NMZKJT7T7607DA9N44N0A8SVTFUYA0S4RTSR94TMTF7K8NG6TLSQ2QDH2K
URI with amount
Build Command
npm run qr -- --amt=0.00001000 --btc='bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u' --ln='lnbc10u1p3pj257pp5yztkwjcz5ftl5laxkav23zmzekaw37zk6kmv80pk4xaev5qhtz7qdpdwd3xger9wd5kwm36yprx7u3qd36kucmgyp282etnv3shjcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqjcewm5cjwz4a6rfjx77c490yced6pemk0upkxhy89cmm7sct66k8gneanwykzgdrwrfje69h9u5u0w57rrcsysas7gadwmzxc8c6t0spjazup6' --file=qr_bip21_non-memo.png
URI
bitcoin:bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u?amount=0.00001&lightning=lnbc10u1p3pj257pp5yztkwjcz5ftl5laxkav23zmzekaw37zk6kmv80pk4xaev5qhtz7qdpdwd3xger9wd5kwm36yprx7u3qd36kucmgyp282etnv3shjcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqjcewm5cjwz4a6rfjx77c490yced6pemk0upkxhy89cmm7sct66k8gneanwykzgdrwrfje69h9u5u0w57rrcsysas7gadwmzxc8c6t0spjazup6
URI with amount (uppercase)
Build Command
npm run qr -- --amt=0.00001000 --btc='BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U' --ln='LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6' --file=qr_bip21_non-memo_uppercase.png
URI
bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?amount=0.00001&lightning=LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6
URI with lightning param only
Build Command
npm run qr -- --btc='bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u' --ln='lnbc10u1p3pj257pp5yztkwjcz5ftl5laxkav23zmzekaw37zk6kmv80pk4xaev5qhtz7qdpdwd3xger9wd5kwm36yprx7u3qd36kucmgyp282etnv3shjcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqjcewm5cjwz4a6rfjx77c490yced6pemk0upkxhy89cmm7sct66k8gneanwykzgdrwrfje69h9u5u0w57rrcsysas7gadwmzxc8c6t0spjazup6' --file=qr_bip21_1-param.png
URI
bitcoin:bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u?lightning=lnbc10u1p3pj257pp5yztkwjcz5ftl5laxkav23zmzekaw37zk6kmv80pk4xaev5qhtz7qdpdwd3xger9wd5kwm36yprx7u3qd36kucmgyp282etnv3shjcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqjcewm5cjwz4a6rfjx77c490yced6pemk0upkxhy89cmm7sct66k8gneanwykzgdrwrfje69h9u5u0w57rrcsysas7gadwmzxc8c6t0spjazup6
URI with lightning param only (uppercase)
Build Command
npm run qr -- --btc='BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U' --ln='LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6' --file=qr_bip21_1-param_uppercase.png
URI
bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?lightning=LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6
@robtex I'll make a PR to change the QR codes on the site as well as add some text to explain the value of uppercasing.
nice research and awesome examples!
just some last minute comments and ideas:
there seems to be good support for BITCOIN:
maybe should throw in that as well?
also i see no point in doing lightning=
in lowercase since it is a brand new parameter. make that one uppercase and we might save another pixel. or why not just LN=
?
it is truly annoying that '?' and =
breaks the flow of alphanumerics, but the :
is supported so BITCOIN:BC1...
part would become one single alphanumeric sequence. so your last example would be
1 BITCOIN:BC1...
in alphanumeric
2 ?
in text
3 LN
(or LIGHTNING
) in alphanumeric
4 =
in text
5 LNBC1...
in alphanumeric
probably 2,3,4 will be short enough to be optimizied into one text-mode sequence ?LN=
without having to shift modes
1 BITCOIN:BC1...
in alphanumeric
2 ?LN=
in text
3 LNBC1...
in alphanumeric
error correction L (my favorite since bech has enough error correction):
BITCOIN:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?LN=LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6
I decided to do some investigation regarding the capitalizing the scheme and parameter, as well as changing from lightning
to LN
. While this is far from exhaustive, I have managed to collect a lot of data on this. What I have observed is that (with my example data) the bitcoin address and lightning invoice contribute the most "weight" to the QR code. Through almost all these variations, simply using bitcoin:BECH32 ... lightning=BECH32
is equally effective as BITCOIN:BECH32 ... LN=BECH32
. The only exception to this is row 4 of my data. I even ran the tests again without lightning invoices out of curiosity. Same deal: upper-casing the scheme has no noticeable effect on QR size.
See data here. For each row, the green outlines indicate the smallest QR dimension (there are many ties per row). Orange outlines indicate the second smallest QR dimension (also many ties).
My conclusion:
- Upper-casing scheme and protocol usually has no noticeable effect.
- Switching to
LN=
may have a very small effect in some situations.LN=
does not seem to have good support. (I tested 6 lightning wallets and none recognizedLN=
).
- Upper-casing the address and invoice have an outsized effect in all situations.
bitcoin:BECH32 ... lightning=BECH32
is the format we are within closest reach to in the industry.- Therefore, we should promote
bitcoin:BECH32 ... lightning=BECH32
.
Personally, I'd rather spend time discussing BOLT12 than trying to save 16 pixels in some situations.
Wow, thorough analysis there. Your recommendation makes sense to me. ACK.
definitely exhaustive enough and also uppercase enough for me. very nice!
wasn't aware there were already wallets supporting this lightning= part, so let's not break them.
ACK
Nice research, makes sense to me!
A shame these iQR codes are proprietary: https://qrcode.meetheed.com/question40.php
"You could take an iQR Code and a QR Code of exactly the same size, and encode a lot more data within the iQR Code (up to 80% more)."