Lainring
Listen with curiosity. Speak with honesty. Act with integrity. The greatest problem with communication is we don’t listen to understand. We listen to reply. When we listen with curiosity, we don’t listen with the intent to reply. We listen for what’s behind the words. Roy T. Bennett, The Light in the Heart
Lainring is a decentralized webring created by the users of Lainchan, an anonymous image board; if you notice missing images refresh the page, the DDoS protection sometimes kicks in on pages with lots of requests (like this one).
Keep in mind that my membership in a webring doesn’t mean I endorse or support the contents of the websites in the said webring.
If you want to be added, go to the current Lainchan thread (12) and post your website there together with a 240x60 button image, requirements are to host a current copy of the webring page and to have some kind of content.
Archived Lainring threads: 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1.
Also, this page requires JavaScript to be enabled in your web browser.
Webring
simple yet effective
Members
The members of the Lainring wrote many articles and the list was last updated on
Clearnet
Some of the websites are offline but they are kept in this section for historical reasons.
Tor
Tor, short for “The Onion Router” is a free and open-source software for enabling anonymous communication. Tor protects personal privacy by concealing a user’s location and usage from anyone performing network surveillance or traffic analysis. It protects the user’s freedom and ability to communicate confidentially through IP address anonymity using Tor exit nodes.
I2P
The Invisible Internet Project (I2P) is an anonymous network layer (implemented as a mix network) that allows for censorship-resistant, peer-to-peer communication. Anonymous connections are achieved by encrypting the user’s traffic (by using end-to-end encryption), and sending it through a volunteer-run network of roughly 55,000 computers distributed around the world.
Feeds
You can download a dynamically-generated OPML file with all the feeds of the current Lainring members.
If you want the feedlist in a way that can be imported into Newsboat (copy the text below into ~/.newsboat/urls
), here it is:
click to expand
or, if you’re using sfeed (copy the text below into ~/.sfeed/sfeedrc
but make sure you read the documentation first):
click to expand
Webmasters
The easiest way to add the banners and links for all the current webring members is to copy the HTML code inside the texfield below and add it to your page. Don’t forget to change banner_img/
path to the directory where the banner images are. Feel free to modify the code in any way that suits your website needs.
HTML code:
click to expand
or, using Markdown:
click to expand
Developers
You might want to use the JSON file here (download it, has all the data inside) and parse it on your website. Alternatively, there is an XML file here, it’s not used (but it’s updated) for now in any way. Banner images available as an archive here.
JSON file format
{
"updated": "2023-02-05T10:46:17",
"items": [{
"url": "website URL, prefixed with http(s)://",
"tor": "Tor hidden service URL",
"i2p": "website I2P URL",
"img": "website banner image, can be PNG, JPEG or GIF",
"title": "website title",
"feed": "website feed URL, can be RSS, JSON, ATOM or OPML",
"offline": true
},
...
]
}
Display data from JSON
The simplest way to consume the JSON using JavaScript is this:
<div id="lainring">... Loading, please wait ...</div>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
/* Try to retrieve the json file */
fetch('lainring.json').then(res => res.json()).then((data) => {
let out = '';
/* For each element in the JSON, build an anchor-image DOM structure */
data.items.forEach(element => {
/* This string is split in multiple lines for readability */
out += '<a title="' + element.title + '" ' +
'href="' + element.url + '">' +
'<img src="' + element.img + '" alt="' + element.title + '" /></a>';
});
/* Inject the DOM structure into the element with the id 'lainring' */
document.getElementById('lainring').innerHTML = out;
}).catch(err => {
/* throw an error */
throw err
});
});
</script>
Same thing in basic PHP:
/* Try to retrieve the json file */
$string = file_get_contents("lainring.json");
if ($string === false) {
/* File doesn't exist, throw an error */
echo 'Err!';
}
/* Decode the json string into an array */
$json = json_decode($string, true);
if ($json === null) {
/* File is gibberish, throw an error */
echo 'Err!';
}
/* For each element in the JSON, build an anchor-image DOM structure
and echo it */
foreach ($json['items'] as $key => $val) {
echo '<a title="' . $val['title'] . '" ' .
'href="' . $val['url'] . '">' .
'<img src="' . $val['img'] . '" alt="' . $val['title'] . '" /></a>';
}
Make sure you download the banner images too and modify the paths to your lainring.json
file and banner images.
If you want to redirect the visitors to a random page of the webring, it’s really easy:
<script>
/* Try to retrieve the json file */
fetch('lainring.json').then(res => res.json()).then((data) => {
/* Get a random element from the JSON */
let random = Math.floor(Math.random() * data.items.length);
/* And redirect the browser to its URL */
window.location.replace(data.items[random].url);
}).catch(err => {
/* throw an error */
throw err
});
</script>
If you want to display a random banner from the webring on your page, you can do it like this:
<div id="lainring">... Loading, please wait ...</div>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
/* Try to retrieve the json file */
fetch('lainring.json').then(res => res.json()).then((data) => {
/* Get a random element from the JSON */
let random = Math.floor(Math.random() * data.items.length);
/* And build the DOM structure for the image and its anchor link */
document.getElementById('lainring').innerHTML =
'<a href="' + data.items[random].url + '">' +
'<img src="' + data.items[random].img +
'" alt="' + data.items[random].title + '" /></a>';
}).catch(err => {
/* throw an error */
throw err
});
});
</script>
And it will look like this, if you refresh the page you will get another banner:
If you want the banner to rotate automatically, let’s say every 10 seconds, you can do it this way:
<div id="lainring">... Loading, please wait ...</div>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
/* Try to retrieve the json file */
fetch('lainring.json').then(res => res.json()).then((data) => {
function refresh_banner() {
/* Get a random element from the JSON */
let random = Math.floor(Math.random() * data.items.length);
/* Build the DOM structure for the image and its anchor link */
document.getElementById('lainring').innerHTML =
'<a href="' + data.items[random].url + '">' +
'<img src="' + data.items[random].img +
'" alt="' + data.items[random].title + '" /></a>';
}
/* Show the initial banner image */
refresh_banner();
/* Refresh the banner image every 10000 milliseconds (10 seconds) */
setInterval(refresh_banner, 10000);
}).catch(err => {
/* throw an error */
throw err
});
});
</script>
And it will look like this (wait 10 seconds for the banner to rotate):
If you want to display a random website (text only, no image) from the webring, on your page:
<div id="lainring">... Loading, please wait ...</div>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
/* Try to retrieve the json file */
fetch('lainring.json').then(res => res.json()).then((data) => {
/* Get a random element from the JSON */
let random = Math.floor(Math.random() * data.items.length);
/* And build the DOM structure for the anchor link */
document.getElementById('lainring').innerHTML =
'<a href="' + data.items[random].url + '">' + data.items[random].title + '</a>';
}).catch(err => {
/* throw an error */
throw err
});
});
</script>
And you get this:
If you want a more webring-like “widget” to display on your website and you don’t want to redirect visitors to this website (which you totally shouldn’t do), you can use this code (not as clean as I would like but it does its job):
<div id="lainring">
<a title="previous website" class="prev" href="#">←</a>
part of <a href="https://sizeof.cat/post/lainring/">Lainring</a>
<a title="random website" class="random" href="#">↻</a>
<a title="next website" class="next" href="#">→</a>
</div>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
/* Try to retrieve the json file */
fetch('lainring.json').then(res => res.json()).then((data) => {
let next;
let prev;
let found = false;
/* Cycle through all the elements in the array searching for the
website we're currently on */
data.items.forEach((element, index) => {
let url = new URL(element.url);
if (url.origin == window.location.origin) {
/* This ugly part is so we don't get out of array bounds. Array index
should not get lower than 0 or higher than the array length. */
prev = (index - 1 < 0) ?
json_data.items[json_data.items.length - 1] :
json_data.items[index - 1];
next = (index + 1 > json_data.items.length - 1) ?
json_data.items[0] :
json_data.items[index + 1];
found = true;
}
});
if (found) {
/* Website we're currently on is part of the webring, we change the
URLs of the previous and next links. */
document.querySelector("#lainring .next").href = next.url;
document.querySelector("#lainring .prev").href = prev.url;
} else {
/* Website we're currently on is not part of the webring, so
we hide the previous and next links, leaving the random link. */
document.querySelector("#lainring .next").style.display = "none";
document.querySelector("#lainring .prev").style.display = "none";
}
/* Add a click event for the random website button */
document.querySelector("#lainring .random").addEventListener("click", function() {
let random = Math.floor(Math.random() * data.items.length);
window.location.replace(data.items[random].url);
}, false);
}).catch(err => {
throw err
});
});
</script>
And you get this: